TimeSpanMap.java

  1. /* Copyright 2002-2018 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.SortedSet;
  21. import java.util.TreeSet;

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

  25. /** Container for objects that apply to spans of time.

  26.  * @param <T> Type of the data.

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

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

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

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

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

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

  83.     }

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

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

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

  121.     }

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

  136.     /** Extract a range of the map.
  137.      * <p>
  138.      * The object returned will be a new independent instance that will contain
  139.      * only the transitions that lie in the specified range.
  140.      * </p>
  141.      * <p>
  142.      * Consider for example a map containing objects O₀ valid before t₁, O₁ valid
  143.      * between t₁ and t₂, O₂ valid between t₂ and t₃, O₃ valid between t₃ and t₄,
  144.      * and O₄ valid after t₄. then calling this method with a {@code start}
  145.      * date between t₁ and t₂ and a {@code end} date between t₃ and t₄
  146.      * will result in a new map containing objects O₁ valid before t₂, O₂
  147.      * valid between t₂ and t₃, and O₃ valid after t₃. The validity of O₁
  148.      * is therefore extended in the past, and the validity of O₃ is extended
  149.      * in the future.
  150.      * </p>
  151.      * @param start earliest date at which a transition is included in the range
  152.      * (may be set to {@link AbsoluteDate#PAST_INFINITY} to keep all early transitions)
  153.      * @param end latest date at which a transition is included in the r
  154.      * (may be set to {@link AbsoluteDate#FUTURE_INFINITY} to keep all late transitions)
  155.      * @return a new instance with all transitions restricted to the specified range
  156.      * @since 9.2
  157.      */
  158.     public TimeSpanMap<T> extractRange(final AbsoluteDate start, final AbsoluteDate end) {

  159.         final NavigableSet<Transition<T>> inRange = data.subSet(new Transition<T>(start, null, null), true,
  160.                                                                 new Transition<T>(end,   null, null), true);
  161.         if (inRange.isEmpty()) {
  162.             // there are no transitions at all in the range
  163.             // we need to pick up the only valid object
  164.             return new TimeSpanMap<>(get(start));
  165.         }

  166.         final TimeSpanMap<T> range = new TimeSpanMap<>(inRange.first().before);
  167.         for (final Transition<T> transition : inRange) {
  168.             range.addValidAfter(transition.after, transition.getDate());
  169.         }

  170.         return range;

  171.     }

  172.     /** Get an unmodifiable view of the sorted transitions.
  173.      * @return unmodifiable view of the sorted transitions
  174.      */
  175.     public SortedSet<Transition<T>> getTransitions() {
  176.         return Collections.unmodifiableSortedSet(data);
  177.     }

  178.     /** Local class holding transition times. */
  179.     public static class Transition<S> implements TimeStamped {

  180.         /** Transition date. */
  181.         private final AbsoluteDate date;

  182.         /** Entry valid before the transition. */
  183.         private final S before;

  184.         /** Entry valid after the transition. */
  185.         private final S after;

  186.         /** Simple constructor.
  187.          * @param date transition date
  188.          * @param before entry valid before the transition
  189.          * @param after entry valid after the transition
  190.          */
  191.         private Transition(final AbsoluteDate date, final S before, final S after) {
  192.             this.date   = date;
  193.             this.before = before;
  194.             this.after  = after;
  195.         }

  196.         /** Get the transition date.
  197.          * @return transition date
  198.          */
  199.         @Override
  200.         public AbsoluteDate getDate() {
  201.             return date;
  202.         }

  203.         /** Get the entry valid before transition.
  204.          * @return entry valid before transition
  205.          */
  206.         public S getBefore() {
  207.             return before;
  208.         }

  209.         /** Get the entry valid after transition.
  210.          * @return entry valid after transition
  211.          */
  212.         public S getAfter() {
  213.             return after;
  214.         }

  215.     }

  216. }