TimeSpanMap.java

  1. /* Copyright 2002-2019 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.J2000_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.      * @param entry entry to add
  57.      * @param latestValidityDate date before which the entry is valid
  58.      * (must be different from <em>all</em> dates already used for transitions)
  59.      */
  60.     public void addValidBefore(final T entry, final AbsoluteDate latestValidityDate) {

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

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

  82.     }

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

  99.         if (data.size() == 1) {
  100.             final Transition<T> single = data.first();
  101.             if (single.getBefore() == single.getAfter()) {
  102.                 // the single entry was a dummy one, without a real transition
  103.                 // we replace it entirely
  104.                 data.clear();
  105.                 data.add(new Transition<T>(earliestValidityDate, single.getBefore(), entry));
  106.                 return;
  107.             }
  108.         }

  109.         final Transition<T> next =
  110.                 data.ceiling(new Transition<T>(earliestValidityDate, entry, null));
  111.         if (next == null) {
  112.             // the new transition will be the last one
  113.             data.add(new Transition<T>(earliestValidityDate, data.last().getAfter(), entry));
  114.         } else {
  115.             // the new transition will be before the next one
  116.             data.remove(next);
  117.             data.add(new Transition<T>(earliestValidityDate, next.getBefore(), entry));
  118.             data.add(new Transition<T>(next.date,            entry,            next.getAfter()));
  119.         }

  120.     }

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

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

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

  178.         final NavigableSet<Transition<T>> inRange = data.subSet(new Transition<T>(start, null, null), true,
  179.                                                                 new Transition<T>(end,   null, null), true);
  180.         if (inRange.isEmpty()) {
  181.             // there are no transitions at all in the range
  182.             // we need to pick up the only valid object
  183.             return new TimeSpanMap<>(get(start));
  184.         }

  185.         final TimeSpanMap<T> range = new TimeSpanMap<>(inRange.first().before);
  186.         for (final Transition<T> transition : inRange) {
  187.             range.addValidAfter(transition.after, transition.getDate());
  188.         }

  189.         return range;

  190.     }

  191.     /** Get an unmodifiable view of the sorted transitions.
  192.      * @return unmodifiable view of the sorted transitions
  193.      */
  194.     public NavigableSet<Transition<T>> getTransitions() {
  195.         return Collections.unmodifiableNavigableSet(data);
  196.     }

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

  208.         /** Transition date. */
  209.         private final AbsoluteDate date;

  210.         /** Entry valid before the transition. */
  211.         private final S before;

  212.         /** Entry valid after the transition. */
  213.         private final S after;

  214.         /** Simple constructor.
  215.          * @param date transition date
  216.          * @param before entry valid before the transition
  217.          * @param after entry valid after the transition
  218.          */
  219.         private Transition(final AbsoluteDate date, final S before, final S after) {
  220.             this.date   = date;
  221.             this.before = before;
  222.             this.after  = after;
  223.         }

  224.         /** Get the transition date.
  225.          * @return transition date
  226.          */
  227.         @Override
  228.         public AbsoluteDate getDate() {
  229.             return date;
  230.         }

  231.         /** Get the entry valid before transition.
  232.          * @return entry valid before transition
  233.          */
  234.         public S getBefore() {
  235.             return before;
  236.         }

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

  243.     }

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

  256.         /** Valid data. */
  257.         private final S data;

  258.         /** Start of validity for the data. */
  259.         private final AbsoluteDate start;

  260.         /** End of validity for the data. */
  261.         private final AbsoluteDate end;

  262.         /** Simple constructor.
  263.          * @param data valid data
  264.          * @param start start of validity for the data
  265.          * @param end end of validity for the data
  266.          */
  267.         private Span(final S data, final AbsoluteDate start, final AbsoluteDate end) {
  268.             this.data  = data;
  269.             this.start = start;
  270.             this.end   = end;
  271.         }

  272.         /** Get the data valid during this time span.
  273.          * @return data valid during this time span
  274.          */
  275.         public S getData() {
  276.             return data;
  277.         }

  278.         /** Get the start of this time span.
  279.          * @return start of this time span
  280.          */
  281.         public AbsoluteDate getStart() {
  282.             return start;
  283.         }

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

  290.     }

  291. }