TimeSpanMap.java

  1. /* Copyright 2002-2020 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.Collections;
  19. import java.util.NavigableSet;
  20. import java.util.TreeSet;

  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.  * @param <T> Type of the data.

  26.  * @author Luc Maisonobe
  27.  * @since 7.1
  28.  */
  29. public class TimeSpanMap<T> {

  30.     /** Container for the data. */
  31.     private final NavigableSet<Transition<T>> data;

  32.     /** Create a map containing a single object, initially valid throughout the timeline.
  33.      * <p>
  34.      * The real validity of this first entry will be truncated as other
  35.      * entries are either {@link #addValidBefore(Object, AbsoluteDate)
  36.      * added before} it or {@link #addValidAfter(Object, AbsoluteDate)
  37.      * added after} it.
  38.      * </p>
  39.      * @param entry entry (initially valid throughout the timeline)
  40.      */
  41.     public TimeSpanMap(final T entry) {
  42.         data = new TreeSet<Transition<T>>(new ChronologicalComparator());
  43.         data.add(new Transition<T>(AbsoluteDate.ARBITRARY_EPOCH, entry, entry));
  44.     }

  45.     /** Add an entry valid before a limit date.
  46.      * <p>
  47.      * As an entry is valid, it truncates the validity of the neighboring
  48.      * entries already present in the map.
  49.      * </p>
  50.      * <p>
  51.      * The transition dates should be entered only once, either
  52.      * by a call to this method or by a call to {@link #addValidAfter(Object,
  53.      * AbsoluteDate)}. Repeating a transition date will lead to unexpected
  54.      * result and is not supported.
  55.      * </p>
  56.      * <p>
  57.      * Using addValidBefore(entry, t) will make 'entry' valid in ]-∞, t[ (note the open bracket).
  58.      * </p>
  59.      * @param entry entry to add
  60.      * @param latestValidityDate date before which the entry is valid
  61.      * (must be different from <em>all</em> dates already used for transitions)
  62.      */
  63.     public void addValidBefore(final T entry, final AbsoluteDate latestValidityDate) {

  64.         if (data.size() == 1) {
  65.             final Transition<T> single = data.first();
  66.             if (single.getBefore() == single.getAfter()) {
  67.                 // the single entry was a dummy one, without a real transition
  68.                 // we replace it entirely
  69.                 data.clear();
  70.                 data.add(new Transition<T>(latestValidityDate, entry, single.getAfter()));
  71.                 return;
  72.             }
  73.         }

  74.         final Transition<T> previous =
  75.                 data.floor(new Transition<T>(latestValidityDate, entry, null));
  76.         if (previous == null) {
  77.             // the new transition will be the first one
  78.             data.add(new Transition<T>(latestValidityDate, entry, data.first().getBefore()));
  79.         } else {
  80.             // the new transition will be after the previous one
  81.             data.remove(previous);
  82.             data.add(new Transition<T>(previous.date,      previous.getBefore(), entry));
  83.             data.add(new Transition<T>(latestValidityDate, entry,                previous.getAfter()));
  84.         }

  85.     }

  86.     /** Add an entry valid after a limit date.
  87.      * <p>
  88.      * As an entry is valid, it truncates the validity of the neighboring
  89.      * entries already present in the map.
  90.      * </p>
  91.      * <p>
  92.      * The transition dates should be entered only once, either
  93.      * by a call to this method or by a call to {@link #addValidBefore(Object,
  94.      * AbsoluteDate)}. Repeating a transition date will lead to unexpected
  95.      * result and is not supported.
  96.      * </p>
  97.      * <p>
  98.      * Using addValidAfter(entry, t) will make 'entry' valid [t, +∞[ (note the closed bracket).
  99.      * </p>
  100.      * @param entry entry to add
  101.      * @param earliestValidityDate date after which the entry is valid
  102.      * (must be different from <em>all</em> dates already used for transitions)
  103.      */
  104.     public void addValidAfter(final T entry, final AbsoluteDate earliestValidityDate) {

  105.         if (data.size() == 1) {
  106.             final Transition<T> single = data.first();
  107.             if (single.getBefore() == single.getAfter()) {
  108.                 // the single entry was a dummy one, without a real transition
  109.                 // we replace it entirely
  110.                 data.clear();
  111.                 data.add(new Transition<T>(earliestValidityDate, single.getBefore(), entry));
  112.                 return;
  113.             }
  114.         }

  115.         final Transition<T> next =
  116.                 data.ceiling(new Transition<T>(earliestValidityDate, entry, null));
  117.         if (next == null) {
  118.             // the new transition will be the last one
  119.             data.add(new Transition<T>(earliestValidityDate, data.last().getAfter(), entry));
  120.         } else {
  121.             // the new transition will be before the next one
  122.             data.remove(next);
  123.             data.add(new Transition<T>(earliestValidityDate, next.getBefore(), entry));
  124.             data.add(new Transition<T>(next.date,            entry,            next.getAfter()));
  125.         }

  126.     }

  127.     /** Get the entry valid at a specified date.
  128.      * @param date date at which the entry must be valid
  129.      * @return valid entry at specified date
  130.      */
  131.     public T get(final AbsoluteDate date) {
  132.         final Transition<T> previous = data.floor(new Transition<T>(date, null, null));
  133.         if (previous == null) {
  134.             // there are no transition before the specified date
  135.             // return the first valid entry
  136.             return data.first().getBefore();
  137.         } else {
  138.             return previous.getAfter();
  139.         }
  140.     }

  141.     /** Get the time span containing a specified date.
  142.      * @param date date belonging to the desired time span
  143.      * @return time span containing the specified date
  144.      * @since 9.3
  145.      */
  146.     public Span<T> getSpan(final AbsoluteDate date) {
  147.         final Transition<T> previous = data.floor(new Transition<T>(date, null, null));
  148.         if (previous == null) {
  149.             // there are no transition before the specified date
  150.             // return the first valid entry
  151.             return new Span<>(data.first().getBefore(),
  152.                               AbsoluteDate.PAST_INFINITY,
  153.                               data.first().getDate());
  154.         } else {
  155.             final Transition<T> next = data.higher(previous);
  156.             return new Span<>(previous.getAfter(),
  157.                               previous.getDate(),
  158.                               next == null ? AbsoluteDate.FUTURE_INFINITY : next.getDate());
  159.         }
  160.     }

  161.     /** Extract a range of the map.
  162.      * <p>
  163.      * The object returned will be a new independent instance that will contain
  164.      * only the transitions that lie in the specified range.
  165.      * </p>
  166.      * <p>
  167.      * Consider for example a map containing objects O₀ valid before t₁, O₁ valid
  168.      * between t₁ and t₂, O₂ valid between t₂ and t₃, O₃ valid between t₃ and t₄,
  169.      * and O₄ valid after t₄. then calling this method with a {@code start}
  170.      * date between t₁ and t₂ and a {@code end} date between t₃ and t₄
  171.      * will result in a new map containing objects O₁ valid before t₂, O₂
  172.      * valid between t₂ and t₃, and O₃ valid after t₃. The validity of O₁
  173.      * is therefore extended in the past, and the validity of O₃ is extended
  174.      * in the future.
  175.      * </p>
  176.      * @param start earliest date at which a transition is included in the range
  177.      * (may be set to {@link AbsoluteDate#PAST_INFINITY} to keep all early transitions)
  178.      * @param end latest date at which a transition is included in the r
  179.      * (may be set to {@link AbsoluteDate#FUTURE_INFINITY} to keep all late transitions)
  180.      * @return a new instance with all transitions restricted to the specified range
  181.      * @since 9.2
  182.      */
  183.     public TimeSpanMap<T> extractRange(final AbsoluteDate start, final AbsoluteDate end) {

  184.         final NavigableSet<Transition<T>> inRange = data.subSet(new Transition<T>(start, null, null), true,
  185.                                                                 new Transition<T>(end,   null, null), true);
  186.         if (inRange.isEmpty()) {
  187.             // there are no transitions at all in the range
  188.             // we need to pick up the only valid object
  189.             return new TimeSpanMap<>(get(start));
  190.         }

  191.         final TimeSpanMap<T> range = new TimeSpanMap<>(inRange.first().before);
  192.         for (final Transition<T> transition : inRange) {
  193.             range.addValidAfter(transition.after, transition.getDate());
  194.         }

  195.         return range;

  196.     }

  197.     /** Get an unmodifiable view of the sorted transitions.
  198.      * @return unmodifiable view of the sorted transitions
  199.      */
  200.     public NavigableSet<Transition<T>> getTransitions() {
  201.         return Collections.unmodifiableNavigableSet(data);
  202.     }

  203.     /** Class holding transition times.
  204.      * <p>
  205.      * This data type is dual to {@link Span}, it is
  206.      * focused one transition date, and gives access to
  207.      * surrounding valid data whereas {@link Span} is focused
  208.      * on one valid data, and gives access to surrounding
  209.      * transition dates.
  210.      * </p>
  211.      * @param <S> Type of the data.
  212.      */
  213.     public static class Transition<S> implements TimeStamped {

  214.         /** Transition date. */
  215.         private final AbsoluteDate date;

  216.         /** Entry valid before the transition. */
  217.         private final S before;

  218.         /** Entry valid after the transition. */
  219.         private final S after;

  220.         /** Simple constructor.
  221.          * @param date transition date
  222.          * @param before entry valid before the transition
  223.          * @param after entry valid after the transition
  224.          */
  225.         private Transition(final AbsoluteDate date, final S before, final S after) {
  226.             this.date   = date;
  227.             this.before = before;
  228.             this.after  = after;
  229.         }

  230.         /** Get the transition date.
  231.          * @return transition date
  232.          */
  233.         @Override
  234.         public AbsoluteDate getDate() {
  235.             return date;
  236.         }

  237.         /** Get the entry valid before transition.
  238.          * @return entry valid before transition
  239.          */
  240.         public S getBefore() {
  241.             return before;
  242.         }

  243.         /** Get the entry valid after transition.
  244.          * @return entry valid after transition
  245.          */
  246.         public S getAfter() {
  247.             return after;
  248.         }

  249.     }

  250.     /** Holder for one time span.
  251.      * <p>
  252.      * This data type is dual to {@link Transition}, it
  253.      * is focused on one valid data, and gives access to
  254.      * surrounding transition dates whereas {@link Transition}
  255.      * is focused on one transition date, and gives access to
  256.      * surrounding valid data.
  257.      * </p>
  258.      * @param <S> Type of the data.
  259.      * @since 9.3
  260.      */
  261.     public static class Span<S> {

  262.         /** Valid data. */
  263.         private final S data;

  264.         /** Start of validity for the data. */
  265.         private final AbsoluteDate start;

  266.         /** End of validity for the data. */
  267.         private final AbsoluteDate end;

  268.         /** Simple constructor.
  269.          * @param data valid data
  270.          * @param start start of validity for the data
  271.          * @param end end of validity for the data
  272.          */
  273.         private Span(final S data, final AbsoluteDate start, final AbsoluteDate end) {
  274.             this.data  = data;
  275.             this.start = start;
  276.             this.end   = end;
  277.         }

  278.         /** Get the data valid during this time span.
  279.          * @return data valid during this time span
  280.          */
  281.         public S getData() {
  282.             return data;
  283.         }

  284.         /** Get the start of this time span.
  285.          * @return start of this time span
  286.          */
  287.         public AbsoluteDate getStart() {
  288.             return start;
  289.         }

  290.         /** Get the end of this time span.
  291.          * @return end of this time span
  292.          */
  293.         public AbsoluteDate getEnd() {
  294.             return end;
  295.         }

  296.     }

  297. }