Skip to content

Commit ecd59d7

Browse files
committed
Make ConcurrentExpandableList an inner class
It is used only by the LogRecorder. Some day, we could migrate this upstream to SciJava Common as a utility class. But for now, let's keep it private, since it would be weird to depend on scijava-ui-swing just to gain access to this class which is not Swing-specific.
1 parent 450d7be commit ecd59d7

File tree

4 files changed

+89
-128
lines changed

4 files changed

+89
-128
lines changed

src/main/java/org/scijava/ui/swing/console/ConcurrentExpandableList.java

Lines changed: 0 additions & 79 deletions
This file was deleted.

src/main/java/org/scijava/ui/swing/console/LogRecorder.java

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,15 @@
3333

3434
import java.util.Iterator;
3535
import java.util.List;
36+
import java.util.Map;
37+
import java.util.NoSuchElementException;
38+
import java.util.Spliterator;
39+
import java.util.Spliterators;
40+
import java.util.concurrent.ConcurrentHashMap;
3641
import java.util.concurrent.CopyOnWriteArrayList;
42+
import java.util.concurrent.atomic.AtomicLong;
3743
import java.util.stream.Stream;
44+
import java.util.stream.StreamSupport;
3845

3946
import org.scijava.log.CallingClassUtils;
4047
import org.scijava.log.IgnoreAsCallingClass;
@@ -54,7 +61,8 @@
5461
@IgnoreAsCallingClass
5562
public class LogRecorder implements LogListener, Iterable<LogMessage> {
5663

57-
private ConcurrentExpandableList recorded = new ConcurrentExpandableList();
64+
private ConcurrentExpandableList<LogMessage> recorded =
65+
new ConcurrentExpandableList<>();
5866

5967
private List<Runnable> observers = new CopyOnWriteArrayList<>();
6068

@@ -79,6 +87,7 @@ public void removeObserver(Runnable observer) {
7987
* {@link Iterator#hasNext()} will return true again, and
8088
* {@link Iterator#next()} will return the new log messages element.
8189
*/
90+
@Override
8291
public Iterator<LogMessage> iterator() {
8392
return recorded.iterator();
8493
}
@@ -122,4 +131,74 @@ private void notifyListeners() {
122131
for (Runnable listener : observers)
123132
listener.run();
124133
}
134+
135+
/**
136+
* This Container manages a list of items. Items can only be added to end of
137+
* the list. It's possible to add items, while iterating over the list.
138+
* Iterators never fail, and they will always be updated. Even if an element
139+
* is added after an iterator reached the end of the list,
140+
* {@link Iterator#hasNext()} will return true again, and
141+
* {@link Iterator#next()} will return the newly added element. This Container
142+
* is fully thread safe.
143+
*
144+
* @author Matthias Arzt
145+
*/
146+
private class ConcurrentExpandableList<T> implements Iterable<T> {
147+
148+
private final AtomicLong lastKey = new AtomicLong(0);
149+
150+
private long firstKey = 0;
151+
152+
private final Map<Long, T> map = new ConcurrentHashMap<>();
153+
154+
public Stream<T> stream() {
155+
Spliterator<T> spliterator = Spliterators.spliteratorUnknownSize(
156+
iterator(), Spliterator.ORDERED);
157+
return StreamSupport.stream(spliterator, /* parallel */ false);
158+
}
159+
160+
@Override
161+
public Iterator<T> iterator() {
162+
return new MyIterator(firstKey);
163+
}
164+
165+
public Iterator<T> iteratorAtEnd() {
166+
return new MyIterator(lastKey.get());
167+
}
168+
169+
public long add(T value) {
170+
long key = lastKey.getAndIncrement();
171+
map.put(key, value);
172+
return key;
173+
}
174+
175+
public void clear() {
176+
map.clear();
177+
firstKey = lastKey.get();
178+
}
179+
180+
private class MyIterator implements Iterator<T> {
181+
182+
private long nextIndex;
183+
184+
public MyIterator(long nextIndex) {
185+
this.nextIndex = nextIndex;
186+
}
187+
188+
@Override
189+
public boolean hasNext() {
190+
return map.containsKey(nextIndex);
191+
}
192+
193+
@Override
194+
public T next() {
195+
T value = map.get(nextIndex);
196+
if(value == null)
197+
throw new NoSuchElementException();
198+
nextIndex++;
199+
return value;
200+
}
201+
}
202+
}
203+
125204
}

src/test/java/org/scijava/ui/swing/console/ConcurrentExpandableListTest.java

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/test/java/org/scijava/ui/swing/console/LoggingDemo.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*/
2424
public class LoggingDemo {
2525

26-
public static void main(String... args) throws InterruptedException {
26+
public static void main(String... args) {
2727
Context context = new Context();
2828
UIService ui = context.service(UIService.class);
2929
ui.showUI();
@@ -71,7 +71,7 @@ public void run() {
7171
privateLogger.addLogListener(panel);
7272

7373
JFrame frame = new JFrame("Plugin that logs");
74-
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
74+
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
7575
frame.setLayout(new MigLayout("","[grow]","[][grow]"));
7676
frame.add(newButton("both log", () -> writeToLogger(logger)), "split");
7777
frame.add(newButton("main log", () -> writeToLogger(logService)));
@@ -81,13 +81,13 @@ public void run() {
8181
frame.setVisible(true);
8282
}
8383

84-
private void writeToLogger(Logger logger) {
85-
logger.error("Error message test");
86-
logger.warn("Text describing a warning");
87-
logger.info("An Information");
88-
logger.debug("Something help debugging");
89-
logger.trace("Trace everything");
90-
logger.log(42, "Whats the best log level");
84+
private void writeToLogger(Logger log) {
85+
log.error("Error message test");
86+
log.warn("Text describing a warning");
87+
log.info("An Information");
88+
log.debug("Something help debugging");
89+
log.trace("Trace everything");
90+
log.log(42, "Whats the best log level");
9191
}
9292

9393
private Component newButton(String title, Runnable action) {

0 commit comments

Comments
 (0)