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 covers 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.     /** First span.
  66.      * @since 13.1
  67.      */
  68.     private Span<T> firstSpan;

  69.     /** Last span.
  70.      * @since 13.1
  71.      */
  72.     private Span<T> lastSpan;

  73.     /** End of early expunged range.
  74.      * @since 13.1
  75.      */
  76.     private AbsoluteDate expungedEarly;

  77.     /** Start of late expunged range.
  78.      * @since 13.1
  79.      */
  80.     private AbsoluteDate expungedLate;

  81.     /** Number of time spans. */
  82.     private int nbSpans;

  83.     /** Maximum number of time spans.
  84.      * @since 13.1
  85.      */
  86.     private int maxNbSpans;

  87.     /** Maximum time range between the earliest and the latest transitions.
  88.      * @since 13.1
  89.      */
  90.     private double maxRange;

  91.     /** Expunge policy.
  92.      * @since 13.1
  93.      */
  94.     private ExpungePolicy expungePolicy;

  95.     /** Create a map containing a single object, initially valid throughout the timeline.
  96.      * <p>
  97.      * The real validity of this first entry will be truncated as other
  98.      * entries are either {@link #addValidBefore(Object, AbsoluteDate, boolean)
  99.      * added before} it or {@link #addValidAfter(Object, AbsoluteDate, boolean)
  100.      * added after} it.
  101.      * </p>
  102.      * <p>
  103.      * The initial {@link #configureExpunge(int, double, ExpungePolicy) expunge policy}
  104.      * is to never expunge any entries, it can be changed afterward by calling
  105.      * {@link #configureExpunge(int, double, ExpungePolicy)}
  106.      * </p>
  107.      * @param entry entry (initially valid throughout the timeline)
  108.      */
  109.     public TimeSpanMap(final T entry) {
  110.         this.current   = new Span<>(entry);
  111.         this.firstSpan = current;
  112.         this.lastSpan  = current;
  113.         this.nbSpans   = 1;
  114.         configureExpunge(Integer.MAX_VALUE, Double.POSITIVE_INFINITY, ExpungePolicy.EXPUNGE_FARTHEST);
  115.     }

  116.     /** Configure (or reconfigure) expunge policy for later additions.
  117.      * <p>
  118.      * When an entry is added to the map (using either {@link #addValidBefore(Object, AbsoluteDate, boolean)},
  119.      * {@link #addValidBetween(Object, AbsoluteDate, AbsoluteDate)}, or
  120.      * {@link #addValidAfter(Object, AbsoluteDate, boolean)} that exceeds the allowed capacity in terms
  121.      * of number of time spans or maximum time range between the earliest and the latest transitions,
  122.      * then exceeding data is expunged according to the {@code expungePolicy}.
  123.      * </p>
  124.      * <p>
  125.      * Note that as the policy depends on the date at which new entries are added, the policy will be enforced
  126.      * only for the <em>next</em> calls to {@link #addValidBefore(Object, AbsoluteDate, boolean)},
  127.      * {@link #addValidBetween(Object, AbsoluteDate, AbsoluteDate)}, and {@link #addValidAfter(Object,
  128.      * AbsoluteDate, boolean)}, it is <em>not</em> enforce immediately.
  129.      * </p>
  130.      * @param newMaxNbSpans maximum number of time spans
  131.      * @param newMaxRange maximum time range between the earliest and the latest transitions
  132.      * @param newExpungePolicy expunge policy to apply when capacity is exceeded
  133.      * @since 13.1
  134.      */
  135.     public synchronized void configureExpunge(final int newMaxNbSpans, final double newMaxRange, final ExpungePolicy newExpungePolicy) {
  136.         this.maxNbSpans    = newMaxNbSpans;
  137.         this.maxRange      = newMaxRange;
  138.         this.expungePolicy = newExpungePolicy;
  139.         this.expungedEarly = AbsoluteDate.PAST_INFINITY;
  140.         this.expungedLate  = AbsoluteDate.FUTURE_INFINITY;
  141.     }

  142.     /** Get the number of spans.
  143.      * <p>
  144.      * The number of spans is always at least 1. The number of transitions
  145.      * is always 1 lower than the number of spans.
  146.      * </p>
  147.      * @return number of spans
  148.      * @since 11.1
  149.      */
  150.     public synchronized int getSpansNumber() {
  151.         return nbSpans;
  152.     }

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

  185.         // update current reference to transition date
  186.         locate(latestValidityDate);

  187.         if (erasesEarlier) {

  188.             // drop everything before date
  189.             current.start = null;

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

  195.         }

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

  197.         final Transition<T> start = current.getStartTransition();
  198.         if (start != null && start.getDate().equals(latestValidityDate)) {
  199.             // the transition at the start of the current span is at the exact same date
  200.             // we update it, without adding a new transition
  201.             if (start.previous() != null) {
  202.                 start.previous().setAfter(span);
  203.             }
  204.             start.setBefore(span);
  205.             updateFirstIfNeeded(span);
  206.         } else {

  207.             if (current.getStartTransition() != null) {
  208.                 current.getStartTransition().setAfter(span);
  209.             }

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

  212.         }

  213.         // we consider the last added transition as the new current one
  214.         current = span;

  215.         expungeOldData(latestValidityDate);

  216.         return span;

  217.     }

  218.     /** Add an entry valid after a limit date.
  219.      * <p>
  220.      * As an entry is valid, it truncates or overrides the validity of the neighboring
  221.      * entries already present in the map.
  222.      * </p>
  223.      * <p>
  224.      * If the map already contains transitions that occur later than {@code earliestValidityDate},
  225.      * the {@code erasesLater} parameter controls what to do with them. Let's consider
  226.      * the time span [tₖ; tₖ₊₁[ associated with entry eₖ that would have been valid at time
  227.      * {@code earliestValidityDate} prior to the call to the method (i.e. tₖ &lt;
  228.      * {@code earliestValidityDate} &lt; tₖ₊₁).
  229.      * </p>
  230.      * <ul>
  231.      *  <li>if {@code erasesLater} is {@code true}, then all later transitions
  232.      *      from and including tₖ₊₁ are erased, and the {@code entry} will be valid from
  233.      *      {@code earliestValidityDate} to future infinity</li>
  234.      *  <li>if {@code erasesLater} is {@code false}, then all later transitions
  235.      *      are preserved, and the {@code entry} will be valid from {@code earliestValidityDate}
  236.      *      to tₖ₊₁</li>
  237.      *  </ul>
  238.      * <p>
  239.      * In both cases, the existing entry eₖ time span will be truncated and will be valid
  240.      * only from tₖ to {@code earliestValidityDate}.
  241.      * </p>
  242.      * @param entry entry to add
  243.      * @param earliestValidityDate date after which the entry is valid
  244.      * @param erasesLater if true, the entry erases all existing transitions
  245.      * that are later than {@code earliestValidityDate}
  246.      * @return span with added entry
  247.      * @since 11.1
  248.      */
  249.     public synchronized Span<T> addValidAfter(final T entry, final AbsoluteDate earliestValidityDate, final boolean erasesLater) {

  250.         // update current reference to transition date
  251.         locate(earliestValidityDate);

  252.         if (erasesLater) {

  253.             // drop everything after date
  254.             current.end = null;

  255.             // update count
  256.             nbSpans = 0;
  257.             for (Span<T> span = current; span != null; span = span.previous()) {
  258.                 ++nbSpans;
  259.             }

  260.         }

  261.         final Span<T> span = new Span<>(entry);
  262.         if (current.getEndTransition() != null) {
  263.             current.getEndTransition().setBefore(span);
  264.         }

  265.         final Transition<T> start = current.getStartTransition();
  266.         if (start != null && start.getDate().equals(earliestValidityDate)) {
  267.             // the transition at the start of the current span is at the exact same date
  268.             // we update it, without adding a new transition
  269.             start.setAfter(span);
  270.             updateLastIfNeeded(span);
  271.         } else {
  272.             // we need to add a new transition somewhere inside the current span
  273.             insertTransition(earliestValidityDate, current, span);
  274.         }

  275.         // we consider the last added transition as the new current one
  276.         current = span;

  277.         // update metadata
  278.         expungeOldData(earliestValidityDate);

  279.         return span;

  280.     }

  281.     /** Add an entry valid between two limit dates.
  282.      * <p>
  283.      * As an entry is valid, it truncates or overrides the validity of the neighboring
  284.      * entries already present in the map.
  285.      * </p>
  286.      * @param entry entry to add
  287.      * @param earliestValidityDate date after which the entry is valid
  288.      * @param latestValidityDate date before which the entry is valid
  289.      * @return span with added entry
  290.      * @since 11.1
  291.      */
  292.     public synchronized Span<T> addValidBetween(final T entry, final AbsoluteDate earliestValidityDate, final AbsoluteDate latestValidityDate) {

  293.         // handle special cases
  294.         if (AbsoluteDate.PAST_INFINITY.equals(earliestValidityDate)) {
  295.             if (AbsoluteDate.FUTURE_INFINITY.equals(latestValidityDate)) {
  296.                 // we wipe everything in the map
  297.                 current   = new Span<>(entry);
  298.                 firstSpan = current;
  299.                 lastSpan  = current;
  300.                 return current;
  301.             } else {
  302.                 // we wipe from past infinity
  303.                 return addValidBefore(entry, latestValidityDate, true);
  304.             }
  305.         } else if (AbsoluteDate.FUTURE_INFINITY.equals(latestValidityDate)) {
  306.             // we wipe up to future infinity
  307.             return addValidAfter(entry, earliestValidityDate, true);
  308.         } else {

  309.             // locate spans at earliest and latest dates
  310.             locate(earliestValidityDate);
  311.             Span<T> latest = current;
  312.             while (latest.getEndTransition() != null && latest.getEnd().isBeforeOrEqualTo(latestValidityDate)) {
  313.                 latest = latest.next();
  314.                 --nbSpans;
  315.             }
  316.             if (latest == current) {
  317.                 // the interval splits one transition in the middle, we need to duplicate the instance
  318.                 latest = new Span<>(current.data);
  319.                 if (current.getEndTransition() != null) {
  320.                     current.getEndTransition().setBefore(latest);
  321.                 }
  322.             }

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

  324.             // manage earliest transition
  325.             final Transition<T> start = current.getStartTransition();
  326.             if (start != null && start.getDate().equals(earliestValidityDate)) {
  327.                 // the transition at the start of the current span is at the exact same date
  328.                 // we update it, without adding a new transition
  329.                 start.setAfter(span);
  330.                 updateLastIfNeeded(span);
  331.             } else {
  332.                 // we need to add a new transition somewhere inside the current span
  333.                 insertTransition(earliestValidityDate, current, span);
  334.             }

  335.             // manage latest transition
  336.             insertTransition(latestValidityDate, span, latest);

  337.             // we consider the last added transition as the new current one
  338.             current = span;

  339.             // update metadata
  340.             final AbsoluteDate midDate = earliestValidityDate.shiftedBy(0.5 * latestValidityDate.durationFrom(earliestValidityDate));
  341.             expungeOldData(midDate);

  342.             return span;

  343.         }

  344.     }

  345.     /** Get the entry valid at a specified date.
  346.      * <p>
  347.      * The expected complexity is O(1) for successive calls with
  348.      * neighboring dates, which is the more frequent use in propagation
  349.      * or orbit determination applications, and O(n) for random calls.
  350.      * </p>
  351.      * @param date date at which the entry must be valid
  352.      * @return valid entry at specified date
  353.      * @see #getSpan(AbsoluteDate)
  354.      */
  355.     public synchronized T get(final AbsoluteDate date) {
  356.         return getSpan(date).getData();
  357.     }

  358.     /** Get the time span containing a specified date.
  359.      * <p>
  360.      * The expected complexity is O(1) for successive calls with
  361.      * neighboring dates, which is the more frequent use in propagation
  362.      * or orbit determination applications, and O(n) for random calls.
  363.      * </p>
  364.      * @param date date belonging to the desired time span
  365.      * @return time span containing the specified date
  366.      * @since 9.3
  367.      */
  368.     public synchronized Span<T> getSpan(final AbsoluteDate date) {

  369.         // safety check
  370.         if (date.isBefore(expungedEarly) || date.isAfter(expungedLate)) {
  371.             throw new OrekitException(OrekitMessages.EXPUNGED_SPAN, date);
  372.         }

  373.         locate(date);
  374.         return current;
  375.     }

  376.     /** Locate the time span containing a specified date.
  377.      * <p>
  378.      * The {@code current} field is updated to the located span.
  379.      * After the method returns, {@code current.getStartTransition()} is either
  380.      * null or its date is before or equal to date, and {@code
  381.      * current.getEndTransition()} is either null or its date is after date.
  382.      * </p>
  383.      * @param date date belonging to the desired time span
  384.      */
  385.     private synchronized void locate(final AbsoluteDate date) {

  386.         while (current.getStart().isAfter(date)) {
  387.             // the current span is too late
  388.             current = current.previous();
  389.         }

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

  391.             final Span<T> next = current.next();
  392.             if (next == null) {
  393.                 // this happens when date is FUTURE_INFINITY
  394.                 return;
  395.             }

  396.             // the current span is too early
  397.             current = next;

  398.         }

  399.     }

  400.     /** Insert a transition.
  401.      * @param date transition date
  402.      * @param before span before transition
  403.      * @param after span after transition
  404.      * @since 11.1
  405.      */
  406.     private void insertTransition(final AbsoluteDate date, final Span<T> before, final Span<T> after) {
  407.         final Transition<T> transition = new Transition<>(this, date);
  408.         transition.setBefore(before);
  409.         transition.setAfter(after);
  410.         updateFirstIfNeeded(before);
  411.         updateLastIfNeeded(after);
  412.         ++nbSpans;
  413.     }

  414.     /** Get the first (earliest) transition.
  415.      * @return first (earliest) transition, or null if there are no transitions
  416.      * @since 11.1
  417.      */
  418.     public synchronized Transition<T> getFirstTransition() {
  419.         return getFirstSpan().getEndTransition();
  420.     }

  421.     /** Get the last (latest) transition.
  422.      * @return last (latest) transition, or null if there are no transitions
  423.      * @since 11.1
  424.      */
  425.     public synchronized Transition<T> getLastTransition() {
  426.         return getLastSpan().getStartTransition();
  427.     }

  428.     /** Get the first (earliest) span.
  429.      * @return first (earliest) span
  430.      * @since 11.1
  431.      */
  432.     public synchronized Span<T> getFirstSpan() {
  433.         return firstSpan;
  434.     }

  435.     /** Get the first (earliest) span with non-null data.
  436.      * @return first (earliest) span with non-null data
  437.      * @since 12.1
  438.      */
  439.     public synchronized Span<T> getFirstNonNullSpan() {
  440.         Span<T> span = getFirstSpan();
  441.         while (span.getData() == null) {
  442.             if (span.getEndTransition() == null) {
  443.                 throw new OrekitException(OrekitMessages.NO_CACHED_ENTRIES);
  444.             }
  445.             span = span.next();
  446.         }
  447.         return span;
  448.     }

  449.     /** Get the last (latest) span.
  450.      * @return last (latest) span
  451.      * @since 11.1
  452.      */
  453.     public synchronized Span<T> getLastSpan() {
  454.         return lastSpan;
  455.     }

  456.     /** Get the last (latest) span with non-null data.
  457.      * @return last (latest) span with non-null data
  458.      * @since 12.1
  459.      */
  460.     public synchronized Span<T> getLastNonNullSpan() {
  461.         Span<T> span = getLastSpan();
  462.         while (span.getData() == null) {
  463.             if (span.getStartTransition() == null) {
  464.                 throw new OrekitException(OrekitMessages.NO_CACHED_ENTRIES);
  465.             }
  466.             span = span.previous();
  467.         }
  468.         return span;
  469.     }

  470.     /** Extract a range of the map.
  471.      * <p>
  472.      * The object returned will be a new independent instance that will contain
  473.      * only the transitions that lie in the specified range.
  474.      * </p>
  475.      * <p>
  476.      * Consider, for example, a map containing objects O₀ valid before t₁, O₁ valid
  477.      * between t₁ and t₂, O₂ valid between t₂ and t₃, O₃ valid between t₃ and t₄,
  478.      * and O₄ valid after t₄. then calling this method with a {@code start}
  479.      * date between t₁ and t₂ and a {@code end} date between t₃ and t₄
  480.      * will result in a new map containing objects O₁ valid before t₂, O₂
  481.      * valid between t₂ and t₃, and O₃ valid after t₃. The validity of O₁
  482.      * is therefore extended in the past, and the validity of O₃ is extended
  483.      * in the future.
  484.      * </p>
  485.      * @param start earliest date at which a transition is included in the range
  486.      * (may be set to {@link AbsoluteDate#PAST_INFINITY} to keep all early transitions)
  487.      * @param end latest date at which a transition is included in the r
  488.      * (may be set to {@link AbsoluteDate#FUTURE_INFINITY} to keep all late transitions)
  489.      * @return a new instance with all transitions restricted to the specified range
  490.      * @since 9.2
  491.      */
  492.     public synchronized TimeSpanMap<T> extractRange(final AbsoluteDate start, final AbsoluteDate end) {

  493.         Span<T> span = getSpan(start);
  494.         final TimeSpanMap<T> range = new TimeSpanMap<>(span.getData());
  495.         while (span.getEndTransition() != null && span.getEndTransition().getDate().isBeforeOrEqualTo(end)) {
  496.             span = span.next();
  497.             range.addValidAfter(span.getData(), span.getStartTransition().getDate(), false);
  498.         }

  499.         return range;

  500.     }

  501.     /**
  502.      * Performs an action for each non-null element of the map.
  503.      * <p>
  504.      * The action is performed chronologically.
  505.      * </p>
  506.      * @param action action to perform on the non-null elements
  507.      * @since 10.3
  508.      */
  509.     public synchronized void forEach(final Consumer<T> action) {
  510.         for (Span<T> span = getFirstSpan(); span != null; span = span.next()) {
  511.             if (span.getData() != null) {
  512.                 action.accept(span.getData());
  513.             }
  514.         }
  515.     }

  516.     /**
  517.      * Expunge old data.
  518.      * @param date date of the latest added data
  519.      */
  520.     private synchronized void expungeOldData(final AbsoluteDate date) {

  521.         while (nbSpans > maxNbSpans || lastSpan.getStart().durationFrom(firstSpan.getEnd()) > maxRange) {
  522.             // capacity exceeded, we need to purge old data
  523.             if (expungePolicy.expungeEarliest(date, firstSpan.getEnd(), lastSpan.getStart())) {
  524.                 // we need to purge the earliest data
  525.                 if (firstSpan.getEnd().isAfter(expungedEarly)) {
  526.                     expungedEarly  = firstSpan.getEnd();
  527.                 }
  528.                 firstSpan       = firstSpan.next();
  529.                 firstSpan.start = null;
  530.                 if (current.start == null) {
  531.                     // the current span was the one we just expunged
  532.                     // we need to update it
  533.                     current = firstSpan;
  534.                 }
  535.             } else {
  536.                 // we need to purge the latest data
  537.                 if (lastSpan.getStart().isBefore(expungedLate)) {
  538.                     expungedLate = lastSpan.getStart();
  539.                 }
  540.                 lastSpan     = lastSpan.previous();
  541.                 lastSpan.end = null;
  542.                 if (current.end == null) {
  543.                     // the current span was the one we just expunged
  544.                     // we need to update it
  545.                     current = lastSpan;
  546.                 }
  547.             }
  548.             --nbSpans;
  549.         }

  550.     }

  551.     /** Update first span if needed.
  552.      * @param candidate candidate first span
  553.      * @since 13.1
  554.      */
  555.     private void updateFirstIfNeeded(final Span<T> candidate) {
  556.         if (candidate.getStartTransition() == null) {
  557.             firstSpan = candidate;
  558.         }
  559.     }

  560.     /** Update last span if needed.
  561.      * @param candidate candidate last span
  562.      * @since 13.1
  563.      */
  564.     private void updateLastIfNeeded(final Span<T> candidate) {
  565.         if (candidate.getEndTransition() == null) {
  566.             lastSpan = candidate;
  567.         }
  568.     }

  569.     /** Class holding transition times.
  570.      * <p>
  571.      * This data type is dual to {@link Span}, it is
  572.      * focused on one transition date, and gives access to
  573.      * surrounding valid data whereas {@link Span} is focused
  574.      * on one valid data, and gives access to surrounding
  575.      * transition dates.
  576.      * </p>
  577.      * @param <S> Type of the data.
  578.      */
  579.     public static class Transition<S> implements TimeStamped {

  580.         /** Map this transition belongs to.
  581.          * @since 13.0
  582.          */
  583.         private final TimeSpanMap<S> map;

  584.         /** Transition date. */
  585.         private AbsoluteDate date;

  586.         /** Entry valid before the transition. */
  587.         private Span<S> before;

  588.         /** Entry valid after the transition. */
  589.         private Span<S> after;

  590.         /** Simple constructor.
  591.          * @param map map this transition belongs to
  592.          * @param date transition date
  593.          */
  594.         private Transition(final TimeSpanMap<S> map, final AbsoluteDate date) {
  595.             this.map  = map;
  596.             this.date = date;
  597.         }

  598.         /** Set the span valid before transition.
  599.          * @param before span valid before transition (must be non-null)
  600.          */
  601.         void setBefore(final Span<S> before) {
  602.             this.before = before;
  603.             before.end  = this;
  604.         }

  605.         /** Set the span valid after transition.
  606.          * @param after span valid after transition (must be non-null)
  607.          */
  608.         void setAfter(final Span<S> after) {
  609.             this.after  = after;
  610.             after.start = this;
  611.         }

  612.         /** Get the transition date.
  613.          * @return transition date
  614.          */
  615.         @Override
  616.         public AbsoluteDate getDate() {
  617.             return date;
  618.         }

  619.         /** Move transition.
  620.          * <p>
  621.          * When moving a transition to past or future infinity, it will be disconnected
  622.          * from the time span it initially belonged to as the next or previous time
  623.          * span validity will be extends to infinity.
  624.          * </p>
  625.          * @param newDate new transition date
  626.          * @param eraseOverridden if true, spans that are entirely between current
  627.          * and new transition dates will be silently removed, if false and such
  628.          * spans exist, an exception will be triggered
  629.          * @since 13.0
  630.          */
  631.         public void resetDate(final AbsoluteDate newDate, final boolean eraseOverridden) {
  632.             if (newDate.isAfter(date)) {
  633.                 // we are moving the transition towards future

  634.                 // find span after new date
  635.                 Span<S> newAfter = after;
  636.                 while (newAfter.getEndTransition() != null &&
  637.                        newAfter.getEndTransition().getDate().isBeforeOrEqualTo(newDate)) {
  638.                     if (eraseOverridden) {
  639.                         map.nbSpans--;
  640.                     } else {
  641.                         // forbidden collision detected
  642.                         throw new OrekitException(OrekitMessages.TRANSITION_DATES_COLLISION,
  643.                                                   date, newDate, newAfter.getEndTransition().getDate());
  644.                     }
  645.                     newAfter = newAfter.next();
  646.                 }

  647.                 synchronized (map) {
  648.                     // perform update
  649.                     date = newDate;
  650.                     after = newAfter;
  651.                     after.start = this;
  652.                     map.current = before;

  653.                     if (newDate.isInfinite()) {
  654.                         // we have just moved the transition to future infinity, it should really disappear
  655.                         map.nbSpans--;
  656.                         map.lastSpan = before;
  657.                         before.end   = null;
  658.                     }
  659.                 }

  660.             } else {
  661.                 // we are moving transition towards the past

  662.                 // find span before new date
  663.                 Span<S> newBefore = before;
  664.                 while (newBefore.getStartTransition() != null &&
  665.                        newBefore.getStartTransition().getDate().isAfterOrEqualTo(newDate)) {
  666.                     if (eraseOverridden) {
  667.                         map.nbSpans--;
  668.                     } else {
  669.                         // forbidden collision detected
  670.                         throw new OrekitException(OrekitMessages.TRANSITION_DATES_COLLISION,
  671.                                                   date, newDate, newBefore.getStartTransition().getDate());
  672.                     }
  673.                     newBefore = newBefore.previous();
  674.                 }

  675.                 synchronized (map) {
  676.                     // perform update
  677.                     date = newDate;
  678.                     before = newBefore;
  679.                     before.end = this;
  680.                     map.current = after;

  681.                     if (newDate.isInfinite()) {
  682.                         // we have just moved the transition to past infinity, it should really disappear
  683.                         map.nbSpans--;
  684.                         map.firstSpan = after;
  685.                         after.start   = null;
  686.                     }
  687.                 }

  688.             }
  689.         }

  690.         /** Get the previous transition.
  691.          * @return previous transition, or null if this transition was the first one
  692.          * @since 11.1
  693.          */
  694.         public Transition<S> previous() {
  695.             return before.getStartTransition();
  696.         }

  697.         /** Get the next transition.
  698.          * @return next transition, or null if this transition was the last one
  699.          * @since 11.1
  700.          */
  701.         public Transition<S> next() {
  702.             return after.getEndTransition();
  703.         }

  704.         /** Get the entry valid before transition.
  705.          * @return entry valid before transition
  706.          * @see #getSpanBefore()
  707.          */
  708.         public S getBefore() {
  709.             return before.getData();
  710.         }

  711.         /** Get the {@link Span} valid before transition.
  712.          * @return {@link Span} valid before transition
  713.          * @since 11.1
  714.          */
  715.         public Span<S> getSpanBefore() {
  716.             return before;
  717.         }

  718.         /** Get the entry valid after transition.
  719.          * @return entry valid after transition
  720.          * @see #getSpanAfter()
  721.          */
  722.         public S getAfter() {
  723.             return after.getData();
  724.         }

  725.         /** Get the {@link Span} valid after transition.
  726.          * @return {@link Span} valid after transition
  727.          * @since 11.1
  728.          */
  729.         public Span<S> getSpanAfter() {
  730.             return after;
  731.         }

  732.     }

  733.     /** Holder for one time span.
  734.      * <p>
  735.      * This data type is dual to {@link Transition}, it
  736.      * is focused on one valid data, and gives access to
  737.      * surrounding transition dates whereas {@link Transition}
  738.      * is focused on one transition date, and gives access to
  739.      * surrounding valid data.
  740.      * </p>
  741.      * @param <S> Type of the data.
  742.      * @since 9.3
  743.      */
  744.     public static class Span<S> {

  745.         /** Valid data. */
  746.         private final S data;

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

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

  751.         /** Simple constructor.
  752.          * @param data valid data
  753.          */
  754.         private Span(final S data) {
  755.             this.data = data;
  756.         }

  757.         /** Get the data valid during this time span.
  758.          * @return data valid during this time span
  759.          */
  760.         public S getData() {
  761.             return data;
  762.         }

  763.         /** Get the previous time span.
  764.          * @return previous time span, or null if this time span was the first one
  765.          * @since 11.1
  766.          */
  767.         public Span<S> previous() {
  768.             return start == null ? null : start.getSpanBefore();
  769.         }

  770.         /** Get the next time span.
  771.          * @return next time span, or null if this time span was the last one
  772.          * @since 11.1
  773.          */
  774.         public Span<S> next() {
  775.             return end == null ? null : end.getSpanAfter();
  776.         }

  777.         /** Get the start of this time span.
  778.          * @return start of this time span (will be {@link AbsoluteDate#PAST_INFINITY}
  779.          * if {@link #getStartTransition()} returns null)
  780.          * @see #getStartTransition()
  781.          */
  782.         public AbsoluteDate getStart() {
  783.             return start == null ? AbsoluteDate.PAST_INFINITY : start.getDate();
  784.         }

  785.         /** Get the transition at the start of this time span.
  786.          * @return transition at the start of this time span (null if span extends to past infinity)
  787.          * @see #getStart()
  788.          * @since 11.1
  789.          */
  790.         public Transition<S> getStartTransition() {
  791.             return start;
  792.         }

  793.         /** Get the end of this time span.
  794.          * @return end of this time span (will be {@link AbsoluteDate#FUTURE_INFINITY}
  795.          * if {@link #getEndTransition()} returns null)
  796.          * @see #getEndTransition()
  797.          */
  798.         public AbsoluteDate getEnd() {
  799.             return end == null ? AbsoluteDate.FUTURE_INFINITY : end.getDate();
  800.         }

  801.         /** Get the transition at the end of this time span.
  802.          * @return transition at the end of this time span (null if span extends to future infinity)
  803.          * @see #getEnd()
  804.          * @since 11.1
  805.          */
  806.         public Transition<S> getEndTransition() {
  807.             return end;
  808.         }

  809.     }

  810. }