-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGenerator.java
More file actions
154 lines (139 loc) · 4.81 KB
/
Generator.java
File metadata and controls
154 lines (139 loc) · 4.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package javaxt.utils;
import java.util.Iterator;
import java.util.NoSuchElementException;
//******************************************************************************
//** Generator
//******************************************************************************
/**
* A custom iterator that yields its values one at a time. This is a great
* alternative to arrays or lists when dealing with large data. By yielding
* one entry at a time, this iterator can help avoid out of memory exceptions.
*
* Subclasses must define a method called {@link #run()} and may call
* {@link yield(T)} to return values one at a time. Example:
<pre>
Generator<String> generator = new Generator<String>() {
@Override
public void run() {
BufferedReader br = file.getBufferedReader("UTF-8");
String row;
while ((row = br.readLine()) != null){
yield(row);
}
br.close();
}
};
</pre>
*
* Clients can iterate through the generated results using standard iterators
* or an enhanced for loop like this:
<pre>
for (String row : generator){
System.out.println(row);
}
</pre>
*
* @author Michael Herrmann
* https://github.com/mherrmann/java-generator-functions
*
******************************************************************************/
public abstract class Generator<T> implements Iterable<T>, AutoCloseable {
private class Condition {
private boolean isSet;
public synchronized void set() {
isSet = true;
notify();
}
public synchronized void await() throws InterruptedException {
try {
if (isSet)
return;
wait();
} finally {
isSet = false;
}
}
}
static ThreadGroup THREAD_GROUP;
Thread producer;
private boolean hasFinished;
private final Condition itemAvailableOrHasFinished = new Condition();
private final Condition itemRequested = new Condition();
private T nextItem;
private boolean nextItemAvailable;
private RuntimeException exceptionRaisedByProducer;
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
@Override
public boolean hasNext() {
return waitForNext();
}
@Override
public T next() {
if (!waitForNext())
throw new NoSuchElementException();
nextItemAvailable = false;
return nextItem;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
private boolean waitForNext() {
if (nextItemAvailable)
return true;
if (hasFinished)
return false;
if (producer == null)
startProducer();
itemRequested.set();
try {
itemAvailableOrHasFinished.await();
} catch (InterruptedException e) {
hasFinished = true;
}
if (exceptionRaisedByProducer != null)
throw exceptionRaisedByProducer;
return !hasFinished;
}
};
}
public void close(){}
protected abstract void run() throws InterruptedException;
protected void yield(T element) throws InterruptedException {
nextItem = element;
nextItemAvailable = true;
itemAvailableOrHasFinished.set();
itemRequested.await();
}
private void startProducer() {
assert producer == null;
if (THREAD_GROUP == null)
THREAD_GROUP = new ThreadGroup("generatorfunctions");
producer = new Thread(THREAD_GROUP, new Runnable() {
@Override
public void run() {
try {
itemRequested.await();
Generator.this.run();
} catch (InterruptedException e) {
// No need to do anything here; Remaining steps in run()
// will cleanly shut down the thread.
} catch (RuntimeException e) {
exceptionRaisedByProducer = e;
}
hasFinished = true;
itemAvailableOrHasFinished.set();
}
});
producer.setDaemon(true);
producer.start();
}
@Override
protected void finalize() throws Throwable {
producer.interrupt();
producer.join();
super.finalize();
}
}