TimeSpanMap.java

  1. /* Copyright 2002-2025 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.function.Consumer;

  19. import org.orekit.errors.OrekitException;
  20. import org.orekit.errors.OrekitMessages;
  21. import org.orekit.time.AbsoluteDate;
  22. import org.orekit.time.TimeStamped;

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

  63.     /** Reference to last accessed data. */
  64.     private Span<T> current;

  65.     /** Number of time spans. */
  66.     private int nbSpans;

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

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

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

  123.         // update current reference to transition date
  124.         locate(latestValidityDate);

  125.         if (erasesEarlier) {

  126.             // drop everything before date
  127.             current.start = null;

  128.             // update count
  129.             nbSpans = 0;
  130.             for (Span<T> span = current; span != null; span = span.next()) {
  131.                 ++nbSpans;
  132.             }

  133.         }

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

  135.         final Transition<T> start = current.getStartTransition();
  136.         if (start != null && start.getDate().equals(latestValidityDate)) {
  137.             // the transition at start of the current span is at the exact same date
  138.             // we update it, without adding a new transition
  139.             if (start.previous() != null) {
  140.                 start.previous().setAfter(span);
  141.             }
  142.             start.setBefore(span);
  143.         } else {

  144.             if (current.getStartTransition() != null) {
  145.                 current.getStartTransition().setAfter(span);
  146.             }

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

  149.         }

  150.         // we consider the last added transition as the new current one
  151.         current = span;

  152.         return span;

  153.     }

  154.     /** Add an entry valid after a limit date.
  155.      * <p>
  156.      * As an entry is valid, it truncates or overrides the validity of the neighboring
  157.      * entries already present in the map.
  158.      * </p>
  159.      * <p>
  160.      * If the map already contains transitions that occur later than {@code earliestValidityDate},
  161.      * the {@code erasesLater} parameter controls what to do with them. Lets consider
  162.      * the time span [tₖ ; tₖ₊₁[ associated with entry eₖ that would have been valid at time
  163.      * {@code earliestValidityDate} prior to the call to the method (i.e. tₖ &lt;
  164.      * {@code earliestValidityDate} &lt; tₖ₊₁).
  165.      * </p>
  166.      * <ul>
  167.      *  <li>if {@code erasesLater} is {@code true}, then all later transitions
  168.      *      from and including tₖ₊₁ are erased, and the {@code entry} will be valid from
  169.      *      {@code earliestValidityDate} to future infinity</li>
  170.      *  <li>if {@code erasesLater} is {@code false}, then all later transitions
  171.      *      are preserved, and the {@code entry} will be valid from {@code earliestValidityDate}
  172.      *      to tₖ₊₁</li>
  173.      *  </ul>
  174.      * <p>
  175.      * In both cases, the existing entry eₖ time span will be truncated and will be valid
  176.      * only from tₖ to {@code earliestValidityDate}.
  177.      * </p>
  178.      * @param entry entry to add
  179.      * @param earliestValidityDate date after which the entry is valid
  180.      * @param erasesLater if true, the entry erases all existing transitions
  181.      * that are later than {@code earliestValidityDate}
  182.      * @return span with added entry
  183.      * @since 11.1
  184.      */
  185.     public synchronized Span<T> addValidAfter(final T entry, final AbsoluteDate earliestValidityDate, final boolean erasesLater) {

  186.         // update current reference to transition date
  187.         locate(earliestValidityDate);

  188.         if (erasesLater) {

  189.             // drop everything after date
  190.             current.end = null;

  191.             // update count
  192.             nbSpans = 0;
  193.             for (Span<T> span = current; span != null; span = span.previous()) {
  194.                 ++nbSpans;
  195.             }

  196.         }

  197.         final Span<T> span = new Span<>(entry);
  198.         if (current.getEndTransition() != null) {
  199.             current.getEndTransition().setBefore(span);
  200.         }

  201.         final Transition<T> start = current.getStartTransition();
  202.         if (start != null && start.getDate().equals(earliestValidityDate)) {
  203.             // the transition at start of the current span is at the exact same date
  204.             // we update it, without adding a new transition
  205.             start.setAfter(span);
  206.         } else {
  207.             // we need to add a new transition somewhere inside the current span
  208.             insertTransition(earliestValidityDate, current, span);
  209.         }

  210.         // we consider the last added transition as the new current one
  211.         current = span;

  212.         return span;

  213.     }

  214.     /** Add an entry valid between two limit dates.
  215.      * <p>
  216.      * As an entry is valid, it truncates or overrides the validity of the neighboring
  217.      * entries already present in the map.
  218.      * </p>
  219.      * @param entry entry to add
  220.      * @param earliestValidityDate date after which the entry is valid
  221.      * @param latestValidityDate date before which the entry is valid
  222.      * @return span with added entry
  223.      * @since 11.1
  224.      */
  225.     public synchronized Span<T> addValidBetween(final T entry, final AbsoluteDate earliestValidityDate, final AbsoluteDate latestValidityDate) {

  226.         // handle special cases
  227.         if (AbsoluteDate.PAST_INFINITY.equals(earliestValidityDate)) {
  228.             if (AbsoluteDate.FUTURE_INFINITY.equals(latestValidityDate)) {
  229.                 // we wipe everything in the map
  230.                 current = new Span<>(entry);
  231.                 return current;
  232.             } else {
  233.                 // we wipe from past infinity
  234.                 return addValidBefore(entry, latestValidityDate, true);
  235.             }
  236.         } else if (AbsoluteDate.FUTURE_INFINITY.equals(latestValidityDate)) {
  237.             // we wipe up to future infinity
  238.             return addValidAfter(entry, earliestValidityDate, true);
  239.         } else {

  240.             // locate spans at earliest and latest dates
  241.             locate(earliestValidityDate);
  242.             Span<T> latest = current;
  243.             while (latest.getEndTransition() != null && latest.getEnd().isBeforeOrEqualTo(latestValidityDate)) {
  244.                 latest = latest.next();
  245.                 --nbSpans;
  246.             }
  247.             if (latest == current) {
  248.                 // the interval splits one transition in the middle, we need to duplicate the instance
  249.                 latest = new Span<>(current.data);
  250.                 if (current.getEndTransition() != null) {
  251.                     current.getEndTransition().setBefore(latest);
  252.                 }
  253.             }

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

  255.             // manage earliest transition
  256.             final Transition<T> start = current.getStartTransition();
  257.             if (start != null && start.getDate().equals(earliestValidityDate)) {
  258.                 // the transition at start of the current span is at the exact same date
  259.                 // we update it, without adding a new transition
  260.                 start.setAfter(span);
  261.             } else {
  262.                 // we need to add a new transition somewhere inside the current span
  263.                 insertTransition(earliestValidityDate, current, span);
  264.             }

  265.             // manage latest transition
  266.             insertTransition(latestValidityDate, span, latest);

  267.             // we consider the last added transition as the new current one
  268.             current = span;

  269.             return span;

  270.         }

  271.     }

  272.     /** Get the entry valid at a specified date.
  273.      * <p>
  274.      * The expected complexity is O(1) for successive calls with
  275.      * neighboring dates, which is the more frequent use in propagation
  276.      * or orbit determination applications, and O(n) for random calls.
  277.      * </p>
  278.      * @param date date at which the entry must be valid
  279.      * @return valid entry at specified date
  280.      * @see #getSpan(AbsoluteDate)
  281.      */
  282.     public synchronized T get(final AbsoluteDate date) {
  283.         return getSpan(date).getData();
  284.     }

  285.     /** Get the time span containing a specified date.
  286.      * <p>
  287.      * The expected complexity is O(1) for successive calls with
  288.      * neighboring dates, which is the more frequent use in propagation
  289.      * or orbit determination applications, and O(n) for random calls.
  290.      * </p>
  291.      * @param date date belonging to the desired time span
  292.      * @return time span containing the specified date
  293.      * @since 9.3
  294.      */
  295.     public synchronized Span<T> getSpan(final AbsoluteDate date) {
  296.         locate(date);
  297.         return current;
  298.     }

  299.     /** Locate the time span containing a specified date.
  300.      * <p>
  301.      * The {@code current} field is updated to the located span.
  302.      * After the method returns, {@code current.getStartTransition()} is either
  303.      * null or its date is before or equal to date, and {@code
  304.      * current.getEndTransition()} is either null or its date is after date.
  305.      * </p>
  306.      * @param date date belonging to the desired time span
  307.      */
  308.     private synchronized void locate(final AbsoluteDate date) {

  309.         while (current.getStart().isAfter(date)) {
  310.             // current span is too late
  311.             current = current.previous();
  312.         }

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

  314.             final Span<T> next = current.next();
  315.             if (next == null) {
  316.                 // this happens when date is FUTURE_INFINITY
  317.                 return;
  318.             }

  319.             // current span is too early
  320.             current = next;

  321.         }

  322.     }

  323.     /** Insert a transition.
  324.      * @param date transition date
  325.      * @param before span before transition
  326.      * @param after span after transition
  327.      * @since 11.1
  328.      */
  329.     private void insertTransition(final AbsoluteDate date, final Span<T> before, final Span<T> after) {
  330.         final Transition<T> transition = new Transition<>(this, date);
  331.         transition.setBefore(before);
  332.         transition.setAfter(after);
  333.         ++nbSpans;
  334.     }

  335.     /** Get the first (earliest) transition.
  336.      * @return first (earliest) transition, or null if there are no transitions
  337.      * @since 11.1
  338.      */
  339.     public synchronized Transition<T> getFirstTransition() {
  340.         return getFirstSpan().getEndTransition();
  341.     }

  342.     /** Get the last (latest) transition.
  343.      * @return last (latest) transition, or null if there are no transitions
  344.      * @since 11.1
  345.      */
  346.     public synchronized Transition<T> getLastTransition() {
  347.         return getLastSpan().getStartTransition();
  348.     }

  349.     /** Get the first (earliest) span.
  350.      * @return first (earliest) span
  351.      * @since 11.1
  352.      */
  353.     public synchronized Span<T> getFirstSpan() {
  354.         Span<T> span = current;
  355.         while (span.getStartTransition() != null) {
  356.             span = span.previous();
  357.         }
  358.         return span;
  359.     }

  360.     /** Get the first (earliest) span with non-null data.
  361.      * @return first (earliest) span with non-null data
  362.      * @since 12.1
  363.      */
  364.     public synchronized Span<T> getFirstNonNullSpan() {
  365.         Span<T> span = getFirstSpan();
  366.         while (span.getData() == null) {
  367.             if (span.getEndTransition() == null) {
  368.                 throw new OrekitException(OrekitMessages.NO_CACHED_ENTRIES);
  369.             }
  370.             span = span.next();
  371.         }
  372.         return span;
  373.     }

  374.     /** Get the last (latest) span.
  375.      * @return last (latest) span
  376.      * @since 11.1
  377.      */
  378.     public synchronized Span<T> getLastSpan() {
  379.         Span<T> span = current;
  380.         while (span.getEndTransition() != null) {
  381.             span = span.next();
  382.         }
  383.         return span;
  384.     }

  385.     /** Get the last (latest) span with non-null data.
  386.      * @return last (latest) span with non-null data
  387.      * @since 12.1
  388.      */
  389.     public synchronized Span<T> getLastNonNullSpan() {
  390.         Span<T> span = getLastSpan();
  391.         while (span.getData() == null) {
  392.             if (span.getStartTransition() == null) {
  393.                 throw new OrekitException(OrekitMessages.NO_CACHED_ENTRIES);
  394.             }
  395.             span = span.previous();
  396.         }
  397.         return span;
  398.     }

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

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

  428.         return range;

  429.     }

  430.     /**
  431.      * Performs an action for each non-null element of map.
  432.      * <p>
  433.      * The action is performed chronologically.
  434.      * </p>
  435.      * @param action action to perform on the non-null elements
  436.      * @since 10.3
  437.      */
  438.     public synchronized void forEach(final Consumer<T> action) {
  439.         for (Span<T> span = getFirstSpan(); span != null; span = span.next()) {
  440.             if (span.getData() != null) {
  441.                 action.accept(span.getData());
  442.             }
  443.         }
  444.     }

  445.     /** Class holding transition times.
  446.      * <p>
  447.      * This data type is dual to {@link Span}, it is
  448.      * focused on one transition date, and gives access to
  449.      * surrounding valid data whereas {@link Span} is focused
  450.      * on one valid data, and gives access to surrounding
  451.      * transition dates.
  452.      * </p>
  453.      * @param <S> Type of the data.
  454.      */
  455.     public static class Transition<S> implements TimeStamped {

  456.         /** Map this transition belongs to.
  457.          * @since 13.0
  458.          */
  459.         private final TimeSpanMap<S> map;

  460.         /** Transition date. */
  461.         private AbsoluteDate date;

  462.         /** Entry valid before the transition. */
  463.         private Span<S> before;

  464.         /** Entry valid after the transition. */
  465.         private Span<S> after;

  466.         /** Simple constructor.
  467.          * @param map map this transition belongs to
  468.          * @param date transition date
  469.          */
  470.         private Transition(final TimeSpanMap<S> map, final AbsoluteDate date) {
  471.             this.map  = map;
  472.             this.date = date;
  473.         }

  474.         /** Set the span valid before transition.
  475.          * @param before span valid before transition (must be non-null)
  476.          */
  477.         void setBefore(final Span<S> before) {
  478.             this.before = before;
  479.             before.end  = this;
  480.         }

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

  488.         /** Get the transition date.
  489.          * @return transition date
  490.          */
  491.         @Override
  492.         public AbsoluteDate getDate() {
  493.             return date;
  494.         }

  495.         /** Move transition.
  496.          * <p>
  497.          * When moving a transition to past or future infinity, it will be disconnected
  498.          * from the time span it initially belonged to as the next or previous time
  499.          * span validity will be extends to infinity.
  500.          * </p>
  501.          * @param newDate new transition date
  502.          * @param eraseOverridden if true, spans that are entirely between current
  503.          * and new transition dates will be silently removed, if false and such
  504.          * spans exist, an exception will be triggered
  505.          * @since 13.0
  506.          */
  507.         public void resetDate(final AbsoluteDate newDate, final boolean eraseOverridden) {
  508.             if (newDate.isAfter(date)) {
  509.                 // we are moving the transition towards future

  510.                 // find span after new date
  511.                 Span<S> newAfter = after;
  512.                 while (newAfter.getEndTransition() != null &&
  513.                        newAfter.getEndTransition().getDate().isBeforeOrEqualTo(newDate)) {
  514.                     if (eraseOverridden) {
  515.                         map.nbSpans--;
  516.                     } else {
  517.                         // forbidden collision detected
  518.                         throw new OrekitException(OrekitMessages.TRANSITION_DATES_COLLISION,
  519.                                                   date, newDate, newAfter.getEndTransition().getDate());
  520.                     }
  521.                     newAfter = newAfter.next();
  522.                 }

  523.                 synchronized (map) {
  524.                     // perform update
  525.                     date = newDate;
  526.                     after = newAfter;
  527.                     after.start = this;
  528.                     map.current = before;

  529.                     if (newDate.isInfinite()) {
  530.                         // we have just moved the transition to future infinity, it should really disappear
  531.                         map.nbSpans--;
  532.                         before.end = null;
  533.                     }
  534.                 }

  535.             } else {
  536.                 // we are moving transition towards past

  537.                 // find span before new date
  538.                 Span<S> newBefore = before;
  539.                 while (newBefore.getStartTransition() != null &&
  540.                        newBefore.getStartTransition().getDate().isAfterOrEqualTo(newDate)) {
  541.                     if (eraseOverridden) {
  542.                         map.nbSpans--;
  543.                     } else {
  544.                         // forbidden collision detected
  545.                         throw new OrekitException(OrekitMessages.TRANSITION_DATES_COLLISION,
  546.                                                   date, newDate, newBefore.getStartTransition().getDate());
  547.                     }
  548.                     newBefore = newBefore.previous();
  549.                 }

  550.                 synchronized (map) {
  551.                     // perform update
  552.                     date = newDate;
  553.                     before = newBefore;
  554.                     before.end = this;
  555.                     map.current = after;

  556.                     if (newDate.isInfinite()) {
  557.                         // we have just moved the transition to past infinity, it should really disappear
  558.                         map.nbSpans--;
  559.                         after.start = null;
  560.                     }
  561.                 }

  562.             }
  563.         }

  564.         /** Get the previous transition.
  565.          * @return previous transition, or null if this transition was the first one
  566.          * @since 11.1
  567.          */
  568.         public Transition<S> previous() {
  569.             return before.getStartTransition();
  570.         }

  571.         /** Get the next transition.
  572.          * @return next transition, or null if this transition was the last one
  573.          * @since 11.1
  574.          */
  575.         public Transition<S> next() {
  576.             return after.getEndTransition();
  577.         }

  578.         /** Get the entry valid before transition.
  579.          * @return entry valid before transition
  580.          * @see #getSpanBefore()
  581.          */
  582.         public S getBefore() {
  583.             return before.getData();
  584.         }

  585.         /** Get the {@link Span} valid before transition.
  586.          * @return {@link Span} valid before transition
  587.          * @since 11.1
  588.          */
  589.         public Span<S> getSpanBefore() {
  590.             return before;
  591.         }

  592.         /** Get the entry valid after transition.
  593.          * @return entry valid after transition
  594.          * @see #getSpanAfter()
  595.          */
  596.         public S getAfter() {
  597.             return after.getData();
  598.         }

  599.         /** Get the {@link Span} valid after transition.
  600.          * @return {@link Span} valid after transition
  601.          * @since 11.1
  602.          */
  603.         public Span<S> getSpanAfter() {
  604.             return after;
  605.         }

  606.     }

  607.     /** Holder for one time span.
  608.      * <p>
  609.      * This data type is dual to {@link Transition}, it
  610.      * is focused on one valid data, and gives access to
  611.      * surrounding transition dates whereas {@link Transition}
  612.      * is focused on one transition date, and gives access to
  613.      * surrounding valid data.
  614.      * </p>
  615.      * @param <S> Type of the data.
  616.      * @since 9.3
  617.      */
  618.     public static class Span<S> {

  619.         /** Valid data. */
  620.         private final S data;

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

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

  625.         /** Simple constructor.
  626.          * @param data valid data
  627.          */
  628.         private Span(final S data) {
  629.             this.data = data;
  630.         }

  631.         /** Get the data valid during this time span.
  632.          * @return data valid during this time span
  633.          */
  634.         public S getData() {
  635.             return data;
  636.         }

  637.         /** Get the previous time span.
  638.          * @return previous time span, or null if this time span was the first one
  639.          * @since 11.1
  640.          */
  641.         public Span<S> previous() {
  642.             return start == null ? null : start.getSpanBefore();
  643.         }

  644.         /** Get the next time span.
  645.          * @return next time span, or null if this time span was the last one
  646.          * @since 11.1
  647.          */
  648.         public Span<S> next() {
  649.             return end == null ? null : end.getSpanAfter();
  650.         }

  651.         /** Get the start of this time span.
  652.          * @return start of this time span (will be {@link AbsoluteDate#PAST_INFINITY}
  653.          * if {@link #getStartTransition()} returns null)
  654.          * @see #getStartTransition()
  655.          */
  656.         public AbsoluteDate getStart() {
  657.             return start == null ? AbsoluteDate.PAST_INFINITY : start.getDate();
  658.         }

  659.         /** Get the transition at start of this time span.
  660.          * @return transition at start of this time span (null if span extends to past infinity)
  661.          * @see #getStart()
  662.          * @since 11.1
  663.          */
  664.         public Transition<S> getStartTransition() {
  665.             return start;
  666.         }

  667.         /** Get the end of this time span.
  668.          * @return end of this time span (will be {@link AbsoluteDate#FUTURE_INFINITY}
  669.          * if {@link #getEndTransition()} returns null)
  670.          * @see #getEndTransition()
  671.          */
  672.         public AbsoluteDate getEnd() {
  673.             return end == null ? AbsoluteDate.FUTURE_INFINITY : end.getDate();
  674.         }

  675.         /** Get the transition at end of this time span.
  676.          * @return transition at end of this time span (null if span extends to future infinity)
  677.          * @see #getEnd()
  678.          * @since 11.1
  679.          */
  680.         public Transition<S> getEndTransition() {
  681.             return end;
  682.         }

  683.     }

  684. }