package derms.net.rmulticast; import java.net.InetAddress; import java.time.Instant; import java.util.*; import java.util.concurrent.ConcurrentHashMap; class ReceivedSet { private final Map> received; ReceivedSet() { this.received = new ConcurrentHashMap>(); } /** * Add a message to the set if it is not already present. * * @param msg The message to add to the set. * @return True if the set did not already contain the specified message. */ boolean add(Message msg) { return received.put(msg.id(), new Entry(msg)) == null; } Message getByID(MessageID mid) throws NoSuchElementException { Entry e = received.get(mid); if (e == null) throw new NoSuchElementException("message " + mid + " not in received list."); return e.msg; } boolean contains(MessageID mid) { try { Message msg = getByID(mid); return true; } catch (NoSuchElementException e) { return false; } } /** Remove the specified message from the set, if it is present. */ void remove(Message msg) { received.remove(msg.id()); } /** Retrieves, but does not remove, the oldest message, or returns null if the set is empty. */ Message peekOldest() { Entry oldest = null; for (Entry e : received.values()) if (oldest == null || e.isBefore(oldest)) oldest = e; if (oldest == null) return null; return oldest.msg; } Message mostRecentSentBy(InetAddress member) throws NoSuchElementException { Entry recent = null; for (Entry e : received.values()) if (e.msg.isSentBy(member) && (recent == null || e.isAfter(recent))) recent = e; if (recent == null) throw new NoSuchElementException("no message from " + member + " in received list."); return recent.msg; } List> allSentBy(InetAddress sender) { List> sent = new ArrayList>(); for (Entry e : received.values()) { if (e.msg.isSentBy(sender)) sent.add(e.msg); } return sent; } private static class Entry { private final Message msg; private final Instant timestamp; // The time at which the entry was added to the set. private Entry(Message msg) { this.msg = msg; this.timestamp = Instant.now(); } private boolean isBefore(Entry other) { return this.timestamp.isBefore(other.timestamp); } private boolean isAfter(Entry other) { return this.timestamp.isAfter(other.timestamp); } @Override public int hashCode() { return msg.hashCode(); } @Override public boolean equals(Object obj) { return msg.equals(obj); } } }