TimeSpanMap.java

  1. /* Copyright 2002-2022 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.utils;

  18. import java.util.NavigableSet;
  19. import java.util.TreeSet;
  20. import java.util.function.Consumer;

  21. import org.orekit.time.AbsoluteDate;
  22. import org.orekit.time.ChronologicalComparator;
  23. import org.orekit.time.TimeStamped;

  24. /** Container for objects that apply to spans of time.
  25.  * <p>
  26.  * Time span maps can be seen either as an ordered collection of
  27.  * {@link Span time spans} or as an ordered collection
  28.  * of {@link Transition transitions}. Both views are dual one to
  29.  * each other. A time span extends from one transition to the
  30.  * next one, and a transition separates one time span from the
  31.  * next one. Each time span contains one entry that is valid during
  32.  * the time span; this entry may be null if nothing is valid during
  33.  * this time span.
  34.  * </p>
  35.  * <p>
  36.  * Typical uses of {@link TimeSpanMap} are to hold piecewise data, like for
  37.  * example an orbit count that changes at ascending nodes (in which case the
  38.  * entry would be an {@link Integer}), or a visibility status between several
  39.  * objects (in which case the entry would be a {@link Boolean}) or a drag
  40.  * coefficient that is expected to be estimated daily or three-hourly (this is
  41.  * how {@link org.orekit.forces.drag.TimeSpanDragForce TimeSpanDragForce} is
  42.  * implemented).
  43.  * </p>
  44.  * <p>
  45.  * Time span maps are built progressively. At first, they contain one
  46.  * {@link Span time span} only whose validity extends from past infinity to
  47.  * future infinity. Then new entries are added one at a time, associated with
  48.  * transition dates, in order to build up the complete map. The transition dates
  49.  * can be either the start of validity (when calling {@link #addValidAfter(Object,
  50.  * AbsoluteDate, boolean)}), or the end of the validity (when calling {@link
  51.  * #addValidBefore(Object, AbsoluteDate, boolean)}). Entries are often added at one
  52.  * end only (and mainly in chronological order), but this is not required. It is
  53.  * possible for example to first set up a map that cover a large range (say one day),
  54.  * and then to insert intermediate dates using for example propagation and event
  55.  * detectors to carve out some parts. This is akin to the way Binary Space Partitioning
  56.  * Trees work.
  57.  * </p>
  58.  * <p>
  59.  * Since 11.1, this class is thread-safe
  60.  * </p>
  61.  * @param <T> Type of the data.
  62.  * @author Luc Maisonobe
  63.  * @since 7.1
  64.  */
  65. public class TimeSpanMap<T> {

  66.     /** Reference to last accessed data. */
  67.     private Span<T> current;

  68.     /** Number of time spans. */
  69.     private int nbSpans;

  70.     /** Create a map containing a single object, initially valid throughout the timeline.
  71.      * <p>
  72.      * The real validity of this first entry will be truncated as other
  73.      * entries are either {@link #addValidBefore(Object, AbsoluteDate, boolean)
  74.      * added before} it or {@link #addValidAfter(Object, AbsoluteDate, boolean)
  75.      * added after} it.
  76.      * </p>
  77.      * @param entry entry (initially valid throughout the timeline)
  78.      */
  79.     public TimeSpanMap(final T entry) {
  80.         current = new Span<>(entry);
  81.         nbSpans = 1;
  82.     }

  83.     /** Get the number of spans.
  84.      * <p>
  85.      * The number of spans is always at least 1. The number of transitions
  86.      * is always 1 less than the number of spans.
  87.      * </p>
  88.      * @return number of spans
  89.      * @since 11.1
  90.      */
  91.     public synchronized int getSpansNumber() {
  92.         return nbSpans;
  93.     }

  94.     /** Add an entry valid before a limit date.
  95.      * <p>
  96.      * Calling this method is equivalent to call {@link #addValidAfter(Object,
  97.      * AbsoluteDate, boolean) addValidAfter(entry, latestValidityDate, false)}.
  98.      * </p>
  99.      * @param entry entry to add
  100.      * @param latestValidityDate date before which the entry is valid
  101.      * @deprecated as of 11.1, replaced by {@link #addValidBefore(Object, AbsoluteDate, boolean)}
  102.      */
  103.     @Deprecated
  104.     public void addValidBefore(final T entry, final AbsoluteDate latestValidityDate) {
  105.         addValidBefore(entry, latestValidityDate, false);
  106.     }

  107.     /** Add an entry valid before a limit date.
  108.      * <p>
  109.      * As an entry is valid, it truncates or overrides the validity of the neighboring
  110.      * entries already present in the map.
  111.      * </p>
  112.      * <p>
  113.      * If the map already contains transitions that occur earlier than {@code latestValidityDate},
  114.      * the {@code erasesEarlier} parameter controls what to do with them. Lets consider
  115.      * the time span [tₖ ; tₖ₊₁[ associated with entry eₖ that would have been valid at time
  116.      * {@code latestValidityDate} prior to the call to the method (i.e. tₖ &lt;
  117.      * {@code latestValidityDate} &lt; tₖ₊₁).
  118.      * </p>
  119.      * <ul>
  120.      *  <li>if {@code erasesEarlier} is {@code true}, then all earlier transitions
  121.      *      up to and including tₖ are erased, and the {@code entry} will be valid from past infinity
  122.      *      to {@code latestValidityDate}</li>
  123.      *  <li>if {@code erasesEarlier} is {@code false}, then all earlier transitions
  124.      *      are preserved, and the {@code entry} will be valid from tₖ
  125.      *      to {@code latestValidityDate}</li>
  126.      *  </ul>
  127.      * <p>
  128.      * In both cases, the existing entry eₖ time span will be truncated and will be valid
  129.      * only from {@code latestValidityDate} to tₖ₊₁.
  130.      * </p>
  131.      * @param entry entry to add
  132.      * @param latestValidityDate date before which the entry is valid
  133.      * @param erasesEarlier if true, the entry erases all existing transitions
  134.      * that are earlier than {@code latestValidityDate}
  135.      * @return span with added entry
  136.      * @since 11.1
  137.      */
  138.     public synchronized Span<T> addValidBefore(final T entry, final AbsoluteDate latestValidityDate, final boolean erasesEarlier) {

  139.         // update current reference to transition date
  140.         locate(latestValidityDate);

  141.         if (erasesEarlier) {

  142.             // drop everything before date
  143.             current.start = null;

  144.             // update count
  145.             nbSpans = 0;
  146.             for (Span<T> span = current; span != null; span = span.next()) {
  147.                 ++nbSpans;
  148.             }

  149.         }

  150.         final Span<T> span = new Span<>(entry);

  151.         final Transition<T> start = current.getStartTransition();
  152.         if (start != null && start.getDate().equals(latestValidityDate)) {
  153.             // the transition at start of the current span is at the exact same date
  154.             // we update it, without adding a new transition
  155.             if (start.previous() != null) {
  156.                 start.previous().setAfter(span);
  157.             }
  158.             start.setBefore(span);
  159.         } else {

  160.             if (current.getStartTransition() != null) {
  161.                 current.getStartTransition().setAfter(span);
  162.             }

  163.             // we need to add a new transition somewhere inside the current span
  164.             insertTransition(latestValidityDate, span, current);

  165.         }

  166.         // we consider the last added transition as the new current one
  167.         current = span;

  168.         return span;

  169.     }

  170.     /** Add an entry valid after a limit date.
  171.      * <p>
  172.      * Calling this method is equivalent to call {@link #addValidAfter(Object,
  173.      * AbsoluteDate, boolean) addValidAfter(entry, earliestValidityDate, false)}.
  174.      * </p>
  175.      * @param entry entry to add
  176.      * @param earliestValidityDate date after which the entry is valid
  177.      * @deprecated as of 11.1, replaced by {@link #addValidAfter(Object, AbsoluteDate, boolean)}
  178.      */
  179.     @Deprecated
  180.     public void addValidAfter(final T entry, final AbsoluteDate earliestValidityDate) {
  181.         addValidAfter(entry, earliestValidityDate, false);
  182.     }

  183.     /** Add an entry valid after a limit date.
  184.      * <p>
  185.      * As an entry is valid, it truncates or overrides the validity of the neighboring
  186.      * entries already present in the map.
  187.      * </p>
  188.      * <p>
  189.      * If the map already contains transitions that occur earlier than {@code earliestValidityDate},
  190.      * the {@code erasesEarlier} parameter controls what to do with them. Lets consider
  191.      * the time span [tₖ ; tₖ₊₁[ associated with entry eₖ that would have been valid at time
  192.      * {@code earliestValidityDate} prior to the call to the method (i.e. tₖ &lt;
  193.      * {@code earliestValidityDate} &lt; tₖ₊₁).
  194.      * </p>
  195.      * <ul>
  196.      *  <li>if {@code erasesEarlier} is {@code true}, then all earlier transitions
  197.      *      up to and including tₖ are erased, and the {@code entry} will be valid from past infinity
  198.      *      to {@code earliestValidityDate}</li>
  199.      *  <li>if {@code erasesEarlier} is {@code false}, then all earlier transitions
  200.      *      are preserved, and the {@code entry} will be valid from tₖ
  201.      *      to {@code earliestValidityDate}</li>
  202.      *  </ul>
  203.      * <p>
  204.      * In both cases, the existing entry eₖ time span will be truncated and will be valid
  205.      * only from {@code earliestValidityDate} to tₖ₊₁.
  206.      * </p>
  207.      * @param entry entry to add
  208.      * @param earliestValidityDate date after which the entry is valid
  209.      * @param erasesLater if true, the entry erases all existing transitions
  210.      * that are later than {@code earliestValidityDate}
  211.      * @return span with added entry
  212.      * @since 11.1
  213.      */
  214.     public synchronized Span<T> addValidAfter(final T entry, final AbsoluteDate earliestValidityDate, final boolean erasesLater) {

  215.         // update current reference to transition date
  216.         locate(earliestValidityDate);

  217.         if (erasesLater) {

  218.             // drop everything after date
  219.             current.end = null;

  220.             // update count
  221.             nbSpans = 0;
  222.             for (Span<T> span = current; span != null; span = span.previous()) {
  223.                 ++nbSpans;
  224.             }

  225.         }

  226.         final Span<T> span = new Span<>(entry);
  227.         if (current.getEndTransition() != null) {
  228.             current.getEndTransition().setBefore(span);
  229.         }

  230.         final Transition<T> start = current.getStartTransition();
  231.         if (start != null && start.getDate().equals(earliestValidityDate)) {
  232.             // the transition at start of the current span is at the exact same date
  233.             // we update it, without adding a new transition
  234.             start.setAfter(span);
  235.         } else {
  236.             // we need to add a new transition somewhere inside the current span
  237.             insertTransition(earliestValidityDate, current, span);
  238.         }

  239.         // we consider the last added transition as the new current one
  240.         current = span;

  241.         return span;

  242.     }

  243.     /** Add an entry valid between two limit dates.
  244.      * <p>
  245.      * As an entry is valid, it truncates or overrides the validity of the neighboring
  246.      * entries already present in the map.
  247.      * </p>
  248.      * @param entry entry to add
  249.      * @param earliestValidityDate date after which the entry is valid
  250.      * @param latestValidityDate date before which the entry is valid
  251.      * @return span with added entry
  252.      * @since 11.1
  253.      */
  254.     public synchronized Span<T> addValidBetween(final T entry, final AbsoluteDate earliestValidityDate, final AbsoluteDate latestValidityDate) {

  255.         // handle special cases
  256.         if (AbsoluteDate.PAST_INFINITY.equals(earliestValidityDate)) {
  257.             if (AbsoluteDate.FUTURE_INFINITY.equals(latestValidityDate)) {
  258.                 // we wipe everything in the map
  259.                 current = new Span<>(entry);
  260.                 return current;
  261.             } else {
  262.                 // we wipe from past infinity
  263.                 return addValidBefore(entry, latestValidityDate, true);
  264.             }
  265.         } else if (AbsoluteDate.FUTURE_INFINITY.equals(latestValidityDate)) {
  266.             // we wipe up to future infinity
  267.             return addValidAfter(entry, earliestValidityDate, true);
  268.         } else {

  269.             // locate spans at earliest and latest dates
  270.             locate(earliestValidityDate);
  271.             Span<T> latest = current;
  272.             while (latest.getEndTransition() != null && latest.getEnd().isBeforeOrEqualTo(latestValidityDate)) {
  273.                 latest = latest.next();
  274.                 --nbSpans;
  275.             }
  276.             if (latest == current) {
  277.                 // the interval splits one transition in the middle, we need to duplicate the instance
  278.                 latest = new Span<>(current.data);
  279.                 if (current.getEndTransition() != null) {
  280.                     current.getEndTransition().setBefore(latest);
  281.                 }
  282.             }

  283.             final Span<T> span = new Span<>(entry);

  284.             // manage earliest transition
  285.             final Transition<T> start = current.getStartTransition();
  286.             if (start != null && start.getDate().equals(earliestValidityDate)) {
  287.                 // the transition at start of the current span is at the exact same date
  288.                 // we update it, without adding a new transition
  289.                 start.setAfter(span);
  290.             } else {
  291.                 // we need to add a new transition somewhere inside the current span
  292.                 insertTransition(earliestValidityDate, current, span);
  293.             }

  294.             // manage latest transition
  295.             insertTransition(latestValidityDate, span, latest);

  296.             // we consider the last added transition as the new current one
  297.             current = span;

  298.             return span;

  299.         }

  300.     }

  301.     /** Get the entry valid at a specified date.
  302.      * <p>
  303.      * The expected complexity is O(1) for successive calls with
  304.      * neighboring dates, which is the more frequent use in propagation
  305.      * or orbit determination applications, and O(n) for random calls.
  306.      * </p>
  307.      * @param date date at which the entry must be valid
  308.      * @return valid entry at specified date
  309.      * @see #getSpan(AbsoluteDate)
  310.      */
  311.     public synchronized T get(final AbsoluteDate date) {
  312.         return getSpan(date).getData();
  313.     }

  314.     /** Get the time span containing a specified date.
  315.      * <p>
  316.      * The expected complexity is O(1) for successive calls with
  317.      * neighboring dates, which is the more frequent use in propagation
  318.      * or orbit determination applications, and O(n) for random calls.
  319.      * </p>
  320.      * @param date date belonging to the desired time span
  321.      * @return time span containing the specified date
  322.      * @since 9.3
  323.      */
  324.     public synchronized Span<T> getSpan(final AbsoluteDate date) {
  325.         locate(date);
  326.         return current;
  327.     }

  328.     /** Locate the time span containing a specified date.
  329.      * <p>
  330.      * The {@link current} field is updated to the located span.
  331.      * After the method returns, {@code current.getStartTransition()} is either
  332.      * null or its date is before or equal to date, and {@code
  333.      * current.getEndTransition()} is either null or its date is after date.
  334.      * </p>
  335.      * @param date date belonging to the desired time span
  336.      */
  337.     private synchronized void locate(final AbsoluteDate date) {

  338.         while (current.getStart().isAfter(date)) {
  339.             // current span is too late
  340.             current = current.previous();
  341.         }

  342.         while (current.getEnd().isBeforeOrEqualTo(date)) {

  343.             final Span<T> next = current.next();
  344.             if (next == null) {
  345.                 // this happens when date is FUTURE_INFINITY
  346.                 return;
  347.             }

  348.             // current span is too early
  349.             current = next;

  350.         }

  351.     }

  352.     /** Insert a transition.
  353.      * @param date transition date
  354.      * @param before span before transition
  355.      * @param after span after transition
  356.      * @since 11.1
  357.      */
  358.     private void insertTransition(final AbsoluteDate date, final Span<T> before, final Span<T> after) {
  359.         final Transition<T> transition = new Transition<>(date);
  360.         transition.setBefore(before);
  361.         transition.setAfter(after);
  362.         ++nbSpans;
  363.     }

  364.     /** Get the first (earliest) transition.
  365.      * @return first (earliest) transition, or null if there are no transitions
  366.      * @since 11.1
  367.      */
  368.     public synchronized Transition<T> getFirstTransition() {
  369.         return getFirstSpan().getEndTransition();
  370.     }

  371.     /** Get the last (latest) transition.
  372.      * @return last (latest) transition, or null if there are no transitions
  373.      * @since 11.1
  374.      */
  375.     public synchronized Transition<T> getLastTransition() {
  376.         return getLastSpan().getStartTransition();
  377.     }

  378.     /** Get the first (earliest) span.
  379.      * @return first (earliest) span
  380.      * @since 11.1
  381.      */
  382.     public synchronized Span<T> getFirstSpan() {
  383.         Span<T> span = current;
  384.         while (span.getStartTransition() != null) {
  385.             span = span.previous();
  386.         }
  387.         return span;
  388.     }

  389.     /** Get the last (latest) span.
  390.      * @return last (latest) span
  391.      * @since 11.1
  392.      */
  393.     public synchronized Span<T> getLastSpan() {
  394.         Span<T> span = current;
  395.         while (span.getEndTransition() != null) {
  396.             span = span.next();
  397.         }
  398.         return span;
  399.     }

  400.     /** Extract a range of the map.
  401.      * <p>
  402.      * The object returned will be a new independent instance that will contain
  403.      * only the transitions that lie in the specified range.
  404.      * </p>
  405.      * <p>
  406.      * Consider for example a map containing objects O₀ valid before t₁, O₁ valid
  407.      * between t₁ and t₂, O₂ valid between t₂ and t₃, O₃ valid between t₃ and t₄,
  408.      * and O₄ valid after t₄. then calling this method with a {@code start}
  409.      * date between t₁ and t₂ and a {@code end} date between t₃ and t₄
  410.      * will result in a new map containing objects O₁ valid before t₂, O₂
  411.      * valid between t₂ and t₃, and O₃ valid after t₃. The validity of O₁
  412.      * is therefore extended in the past, and the validity of O₃ is extended
  413.      * in the future.
  414.      * </p>
  415.      * @param start earliest date at which a transition is included in the range
  416.      * (may be set to {@link AbsoluteDate#PAST_INFINITY} to keep all early transitions)
  417.      * @param end latest date at which a transition is included in the r
  418.      * (may be set to {@link AbsoluteDate#FUTURE_INFINITY} to keep all late transitions)
  419.      * @return a new instance with all transitions restricted to the specified range
  420.      * @since 9.2
  421.      */
  422.     public synchronized TimeSpanMap<T> extractRange(final AbsoluteDate start, final AbsoluteDate end) {

  423.         Span<T> span = getSpan(start);
  424.         final TimeSpanMap<T> range = new TimeSpanMap<>(span.getData());
  425.         while (span.getEndTransition() != null && span.getEndTransition().getDate().isBeforeOrEqualTo(end)) {
  426.             span = span.next();
  427.             range.addValidAfter(span.getData(), span.getStartTransition().getDate(), false);
  428.         }

  429.         return range;

  430.     }

  431.     /** Get copy of the sorted transitions.
  432.      * @return copy of the sorted transitions
  433.      * @deprecated as of 11.1, replaced by {@link #getFirstSpan()}, {@link #getLastSpan()},
  434.      * {@link #getFirstTransition()}, {@link #getLastTransition()}, and {@link #getSpansNumber()}
  435.      */
  436.     @Deprecated
  437.     public synchronized NavigableSet<Transition<T>> getTransitions() {
  438.         final NavigableSet<Transition<T>> set = new TreeSet<>(new ChronologicalComparator());
  439.         for (Transition<T> transition = getFirstTransition(); transition != null; transition = transition.next()) {
  440.             set.add(transition);
  441.         }
  442.         return set;
  443.     }

  444.     /**
  445.      * Performs an action for each non-null element of map.
  446.      * <p>
  447.      * The action is performed chronologically.
  448.      * </p>
  449.      * @param action action to perform on the non-null elements
  450.      * @since 10.3
  451.      */
  452.     public synchronized void forEach(final Consumer<T> action) {
  453.         for (Span<T> span = getFirstSpan(); span != null; span = span.next()) {
  454.             if (span.getData() != null) {
  455.                 action.accept(span.getData());
  456.             }
  457.         }
  458.     }

  459.     /** Class holding transition times.
  460.      * <p>
  461.      * This data type is dual to {@link Span}, it is
  462.      * focused on one transition date, and gives access to
  463.      * surrounding valid data whereas {@link Span} is focused
  464.      * on one valid data, and gives access to surrounding
  465.      * transition dates.
  466.      * </p>
  467.      * @param <S> Type of the data.
  468.      */
  469.     public static class Transition<S> implements TimeStamped {

  470.         /** Transition date. */
  471.         private AbsoluteDate date;

  472.         /** Entry valid before the transition. */
  473.         private Span<S> before;

  474.         /** Entry valid after the transition. */
  475.         private Span<S> after;

  476.         /** Simple constructor.
  477.          * @param date transition date
  478.          */
  479.         private Transition(final AbsoluteDate date) {
  480.             this.date = date;
  481.         }

  482.         /** Set the span valid before transition.
  483.          * @param before span valid before transition (must be non-null)
  484.          */
  485.         void setBefore(final Span<S> before) {
  486.             this.before = before;
  487.             before.end  = this;
  488.         }

  489.         /** Set the span valid after transition.
  490.          * @param after span valid after transition (must be non-null)
  491.          */
  492.         void setAfter(final Span<S> after) {
  493.             this.after  = after;
  494.             after.start = this;
  495.         }

  496.         /** Get the transition date.
  497.          * @return transition date
  498.          */
  499.         @Override
  500.         public AbsoluteDate getDate() {
  501.             return date;
  502.         }

  503.         /** Get the previous transition.
  504.          * @return previous transition, or null if this transition was the first one
  505.          * @since 11.1
  506.          */
  507.         public Transition<S> previous() {
  508.             return before.getStartTransition();
  509.         }

  510.         /** Get the next transition.
  511.          * @return next transition, or null if this transition was the last one
  512.          * @since 11.1
  513.          */
  514.         public Transition<S> next() {
  515.             return after.getEndTransition();
  516.         }

  517.         /** Get the entry valid before transition.
  518.          * @return entry valid before transition
  519.          * @see #getSpanBefore()
  520.          */
  521.         public S getBefore() {
  522.             return before.getData();
  523.         }

  524.         /** Get the {@link Span} valid before transition.
  525.          * @return {@link Span} valid before transition
  526.          * @since 11.1
  527.          */
  528.         public Span<S> getSpanBefore() {
  529.             return before;
  530.         }

  531.         /** Get the entry valid after transition.
  532.          * @return entry valid after transition
  533.          * @see #getSpanAfter()
  534.          */
  535.         public S getAfter() {
  536.             return after.getData();
  537.         }

  538.         /** Get the {@link Span} valid after transition.
  539.          * @return {@link Span} valid after transition
  540.          * @since 11.1
  541.          */
  542.         public Span<S> getSpanAfter() {
  543.             return after;
  544.         }

  545.     }

  546.     /** Holder for one time span.
  547.      * <p>
  548.      * This data type is dual to {@link Transition}, it
  549.      * is focused on one valid data, and gives access to
  550.      * surrounding transition dates whereas {@link Transition}
  551.      * is focused on one transition date, and gives access to
  552.      * surrounding valid data.
  553.      * </p>
  554.      * @param <S> Type of the data.
  555.      * @since 9.3
  556.      */
  557.     public static class Span<S> {

  558.         /** Valid data. */
  559.         private final S data;

  560.         /** Start of validity for the data (null if span extends to past infinity). */
  561.         private Transition<S> start;

  562.         /** End of validity for the data (null if span extends to future infinity). */
  563.         private Transition<S> end;

  564.         /** Simple constructor.
  565.          * @param data valid data
  566.          */
  567.         private Span(final S data) {
  568.             this.data = data;
  569.         }

  570.         /** Get the data valid during this time span.
  571.          * @return data valid during this time span
  572.          */
  573.         public S getData() {
  574.             return data;
  575.         }

  576.         /** Get the previous time span.
  577.          * @return previous time span, or null if this time span was the first one
  578.          * @since 11.1
  579.          */
  580.         public Span<S> previous() {
  581.             return start == null ? null : start.getSpanBefore();
  582.         }

  583.         /** Get the next time span.
  584.          * @return next time span, or null if this time span was the last one
  585.          * @since 11.1
  586.          */
  587.         public Span<S> next() {
  588.             return end == null ? null : end.getSpanAfter();
  589.         }

  590.         /** Get the start of this time span.
  591.          * @return start of this time span (will be {@link AbsoluteDate#PAST_INFINITY}
  592.          * if {@link #getStartTransition()} returns null)
  593.          * @see #getStartTransition()
  594.          */
  595.         public AbsoluteDate getStart() {
  596.             return start == null ? AbsoluteDate.PAST_INFINITY : start.getDate();
  597.         }

  598.         /** Get the transition at start of this time span.
  599.          * @return transition at start of this time span (null if span extends to past infinity)
  600.          * @see #getStart()
  601.          * @since 11.1
  602.          */
  603.         public Transition<S> getStartTransition() {
  604.             return start;
  605.         }

  606.         /** Get the end of this time span.
  607.          * @return end of this time span (will be {@link AbsoluteDate#FUTURE_INFINITY}
  608.          * if {@link #getEndTransition()} returns null)
  609.          * @see #getEndTransition()
  610.          */
  611.         public AbsoluteDate getEnd() {
  612.             return end == null ? AbsoluteDate.FUTURE_INFINITY : end.getDate();
  613.         }

  614.         /** Get the transition at end of this time span.
  615.          * @return transition at end of this time span (null if span extends to future infinity)
  616.          * @see #getEnd()
  617.          * @since 11.1
  618.          */
  619.         public Transition<S> getEndTransition() {
  620.             return end;
  621.         }

  622.     }

  623. }