001package jmri.jmrix.dccpp.swing.messages; 002 003import java.util.ArrayDeque; 004import java.util.ArrayList; 005import java.util.Collections; 006import java.util.Deque; 007import java.util.List; 008import java.util.function.LongSupplier; 009 010/** Timestamped DCC-EX messages, newest first, with expiry and a line limit; no Swing, thread-safe. */ 011public class MessageQueue { 012 013 public static final int DEFAULT_EXPIRY_SECONDS = 30; 014 public static final int DEFAULT_MAX_LINES = 10; 015 016 private final LongSupplier clock; 017 private final Deque<Entry> entries = new ArrayDeque<>(); 018 private int expirySeconds = DEFAULT_EXPIRY_SECONDS; 019 private int maxLines = DEFAULT_MAX_LINES; 020 021 public MessageQueue() { 022 this(System::currentTimeMillis); 023 } 024 025 public MessageQueue(LongSupplier clock) { 026 this.clock = clock; 027 } 028 029 public synchronized void add(String text) { 030 entries.addFirst(new Entry(clock.getAsLong(), text)); 031 trim(); 032 } 033 034 /** Unmodifiable copy of the messages, newest first. */ 035 public synchronized List<Entry> getEntries() { 036 return Collections.unmodifiableList(new ArrayList<>(entries)); 037 } 038 039 /** Remove messages older than the expiry time; true if any were removed. */ 040 public synchronized boolean expire() { 041 if (expirySeconds <= 0) { 042 return false; 043 } 044 long cutoff = clock.getAsLong() - expirySeconds * 1000L; 045 boolean removed = false; 046 while (!entries.isEmpty() && entries.peekLast().getTimeMillis() <= cutoff) { 047 entries.removeLast(); 048 removed = true; 049 } 050 return removed; 051 } 052 053 public synchronized void clear() { 054 entries.clear(); 055 } 056 057 /** Seconds a message is kept; 0 keeps it until newer messages push it out. */ 058 public synchronized void setExpirySeconds(int seconds) { 059 expirySeconds = Math.max(0, seconds); 060 } 061 062 public synchronized int getExpirySeconds() { 063 return expirySeconds; 064 } 065 066 public synchronized void setMaxLines(int lines) { 067 maxLines = Math.max(1, lines); 068 trim(); 069 } 070 071 public synchronized int getMaxLines() { 072 return maxLines; 073 } 074 075 private void trim() { 076 while (entries.size() > maxLines) { 077 entries.removeLast(); 078 } 079 } 080 081 /** One received message. */ 082 public static final class Entry { 083 084 private final long timeMillis; 085 private final String text; 086 087 Entry(long timeMillis, String text) { 088 this.timeMillis = timeMillis; 089 this.text = text; 090 } 091 092 public long getTimeMillis() { 093 return timeMillis; 094 } 095 096 public String getText() { 097 return text; 098 } 099 } 100 101}