Timeline PriorityQueue now uses stable sorting
[Mograsim.git] / net.mograsim.logic.core / src / net / mograsim / logic / core / timeline / Timeline.java
index 2dd5f97..9c8659c 100644 (file)
@@ -18,26 +18,37 @@ public class Timeline
        private PriorityQueue<InnerEvent> events;
        private LongSupplier time;
        private long lastTimeUpdated = 0;
+       private long eventCounter = 0;
 
        private final List<Consumer<TimelineEvent>> eventAddedListener;
 
+       public final LongSupplier stepByStepExec = () -> lastTimeUpdated;
+       public final LongSupplier realTimeExec = () -> System.currentTimeMillis();
+
+       /**
+        * Constructs a Timeline object. Per default the time function is set to step by step execution.
+        * 
+        * @param initCapacity The initial capacity of the event queue.
+        */
        public Timeline(int initCapacity)
        {
                events = new PriorityQueue<>(initCapacity);
-
                eventAddedListener = new ArrayList<>();
-               time = () -> lastTimeUpdated;
+               time = stepByStepExec;
        }
 
        /**
         * @param timestamp exclusive
-        * @return true if the first event is later than the timestamp
+        * @return <code>true</code> if the first event in queue is later than the given timestamp
         */
        public BooleanSupplier laterThan(long timestamp)
        {
                return () -> timeCmp(events.peek().getTiming(), timestamp) > 0;
        }
 
+       /**
+        * @return <code>true</code> if there is at least one event enqueued. <code>false</code> otherwise
+        */
        public boolean hasNext()
        {
                return !events.isEmpty();
@@ -53,6 +64,9 @@ public class Timeline
                        executeUntil(laterThan(first.getTiming()), -1);
        }
 
+       /**
+        * Executes all events enqueued in the {@link Timeline}. Use very carefully! Events may generate new events, causing an infinite loop.
+        */
        public void executeAll()
        {
                while (hasNext())
@@ -71,7 +85,6 @@ public class Timeline
         * <code>EXEC_UNTIL_CONDITION</code> if the condition was met
         * <code>EXEC_UNTIL_EMPTY</code> if events were executed until the {@link Timeline} was empty
         * @formatter:on
-        * @author Christian Femers, Fabian Stemmler
         */
        public ExecutionResult executeUntil(BooleanSupplier condition, long stopMillis)
        {
@@ -97,16 +110,31 @@ public class Timeline
                return hasNext() ? ExecutionResult.EXEC_UNTIL_EMPTY : ExecutionResult.EXEC_UNTIL_CONDITION;
        }
 
+       /**
+        * Sets the function, which defines the current simulation time at any time.
+        * 
+        * @param time The return value of calling this function is the current simulation time.
+        */
        public void setTimeFunction(LongSupplier time)
        {
                this.time = time;
        }
 
+       /**
+        * Calculates the current simulation time.
+        * 
+        * @return The simulation time as defined by the time function.
+        */
        public long getSimulationTime()
        {
                return time.getAsLong();
        }
 
+       /**
+        * Retrieves the timestamp of the next event.
+        * 
+        * @return The timestamp of the next enqueued event, if the {@link Timeline} is not empty, -1 otherwise.
+        */
        public long nextEventTime()
        {
                if (!hasNext())
@@ -114,17 +142,26 @@ public class Timeline
                return events.peek().getTiming();
        }
 
+       /**
+        * Clears the {@link Timeline} of enqueued events.
+        */
        public void reset()
        {
                events.clear();
                lastTimeUpdated = 0;
        }
 
+       /**
+        * Adds a listener, that is called when a {@link TimelineEvent} is added.
+        */
        public void addEventAddedListener(Consumer<TimelineEvent> listener)
        {
                eventAddedListener.add(listener);
        }
 
+       /**
+        * Removes the listener, if possible. It will no longer be called when a {@link TimelineEvent} is added.
+        */
        public void removeEventAddedListener(Consumer<TimelineEvent> listener)
        {
                eventAddedListener.remove(listener);
@@ -140,7 +177,7 @@ public class Timeline
        {
                long timing = getSimulationTime() + relativeTiming;
                TimelineEvent event = new TimelineEvent(timing);
-               events.add(new InnerEvent(function, event));
+               events.add(new InnerEvent(function, event, eventCounter++));
                eventAddedListener.forEach(l -> l.accept(event));
        }
 
@@ -148,6 +185,7 @@ public class Timeline
        {
                private final TimelineEventHandler function;
                private final TimelineEvent event;
+               private final long id;
 
                /**
                 * Creates an {@link InnerEvent}
@@ -155,10 +193,11 @@ public class Timeline
                 * @param function {@link TimelineEventHandler} to be executed when the {@link InnerEvent} occurs
                 * @param timing   Point in the MI simulation {@link Timeline}, at which the {@link InnerEvent} is executed;
                 */
-               InnerEvent(TimelineEventHandler function, TimelineEvent event)
+               InnerEvent(TimelineEventHandler function, TimelineEvent event, long id)
                {
                        this.function = function;
                        this.event = event;
+                       this.id = id;
                }
 
                public long getTiming()
@@ -181,7 +220,8 @@ public class Timeline
                @Override
                public int compareTo(InnerEvent o)
                {
-                       return timeCmp(getTiming(), o.getTiming());
+                       int c1;
+                       return (c1 = timeCmp(getTiming(), o.getTiming())) == 0 ? timeCmp(id, o.id) : c1;
                }
        }