1   /* Copyright 2002-2021 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  
19  import java.util.Collections;
20  import java.util.NavigableSet;
21  import java.util.SortedSet;
22  import java.util.TreeSet;
23  
24  import org.orekit.time.FieldAbsoluteDate;
25  import org.orekit.time.TimeStamped;
26  import org.hipparchus.Field;
27  import org.hipparchus.CalculusFieldElement;
28  import org.orekit.time.AbsoluteDate;
29  import org.orekit.time.ChronologicalComparator;
30  
31  /** Container for objects that apply to spans of time.
32  
33   * @param <T> Type of the data.
34  
35   * @author Luc Maisonobe
36   * @since 7.1
37   */
38  public class FieldTimeSpanMap<T, D extends CalculusFieldElement<D>> {
39  
40      /** Container for the data. */
41      private final NavigableSet<Transition<T, D>> data;
42  
43      /**Field.*/
44      private final Field<D> field;
45  
46      /** Create a map containing a single object, initially valid throughout the timeline.
47       * <p>
48       * The real validity of this first entry will be truncated as other
49       * entries are either {@link #addValidBefore(Object, FieldAbsoluteDate)
50       * added before} it or {@link #addValidAfter(Object, FieldAbsoluteDate)
51       * added after} it.
52       * </p>
53       * @param entry entry (initially valid throughout the timeline)
54       * @param field_n field used by default.
55       */
56      public FieldTimeSpanMap(final T entry, final Field<D> field_n) {
57          data = new TreeSet<Transition<T, D>>(new ChronologicalComparator());
58          field = field_n;
59          data.add(new Transition<>(FieldAbsoluteDate.getArbitraryEpoch(field), entry, entry));
60      }
61  
62      /** Add an entry valid before a limit date.
63       * <p>
64       * As an entry is valid, it truncates the validity of the neighboring
65       * entries already present in the map.
66       * </p>
67       * <p>
68       * The transition dates should be entered only once, either
69       * by a call to this method or by a call to {@link #addValidAfter(Object,
70       * FieldAbsoluteDate)}. Repeating a transition date will lead to unexpected
71       * result and is not supported.
72       * </p>
73       * @param entry entry to add
74       * @param latestValidityDate date before which the entry is valid
75       * (sould be different from <em>all</em> dates already used for transitions)
76       */
77      public void addValidBefore(final T entry, final FieldAbsoluteDate<D> latestValidityDate) {
78  
79          if (data.size() == 1) {
80              final Transition<T, D> single = data.first();
81              if (single.getBefore() == single.getAfter()) {
82                  // the single entry was a dummy one, without a real transition
83                  // we replace it entirely
84                  data.clear();
85                  data.add(new Transition<T, D>(latestValidityDate, entry, single.getAfter()));
86                  return;
87              }
88          }
89  
90          final Transition<T, D> previous =
91                  data.floor(new Transition<T, D>(latestValidityDate, entry, null));
92          if (previous == null) {
93              // the new transition will be the first one
94              data.add(new Transition<T, D>(latestValidityDate, entry, data.first().getBefore()));
95          } else {
96              // the new transition will be after the previous one
97              data.remove(previous);
98              data.add(new Transition<T, D>(previous.date,      previous.getBefore(), entry));
99              data.add(new Transition<T, D>(latestValidityDate, entry,                previous.getAfter()));
100         }
101 
102     }
103 
104     /** Add an entry valid after a limit date.
105      * <p>
106      * As an entry is valid, it truncates the validity of the neighboring
107      * entries already present in the map.
108      * </p>
109      * <p>
110      * The transition dates should be entered only once, either
111      * by a call to this method or by a call to {@link #addValidBefore(Object,
112      * FieldAbsoluteDate)}. Repeating a transition date will lead to unexpected
113      * result and is not supported.
114      * </p>
115      * @param entry entry to add
116      * @param earliestValidityDate date after which the entry is valid
117      * (sould be different from <em>all</em> dates already used for transitions)
118      */
119     public void addValidAfter(final T entry, final FieldAbsoluteDate<D> earliestValidityDate) {
120 
121         if (data.size() == 1) {
122             final Transition<T, D> single = data.first();
123             if (single.getBefore() == single.getAfter()) {
124                 // the single entry was a dummy one, without a real transition
125                 // we replace it entirely
126                 data.clear();
127                 data.add(new Transition<T, D>(earliestValidityDate, single.getBefore(), entry));
128                 return;
129             }
130         }
131 
132         final Transition<T, D> next =
133                 data.ceiling(new Transition<T, D>(earliestValidityDate, entry, null));
134         if (next == null) {
135             // the new transition will be the last one
136             data.add(new Transition<T, D>(earliestValidityDate, data.last().getAfter(), entry));
137         } else {
138             // the new transition will be before the next one
139             data.remove(next);
140             data.add(new Transition<T, D>(earliestValidityDate, next.getBefore(), entry));
141             data.add(new Transition<T, D>(next.date,            entry,            next.getAfter()));
142         }
143 
144     }
145 
146     /** Get the entry valid at a specified date.
147      * @param date date at which the entry must be valid
148      * @return valid entry at specified date
149      */
150     public T get(final FieldAbsoluteDate<D> date) {
151         final Transition<T, D> previous = data.floor(new Transition<T, D>(date, null, null));
152         if (previous == null) {
153             // there are no transition before the specified date
154             // return the first valid entry
155             return data.first().getBefore();
156         } else {
157             return previous.getAfter();
158         }
159     }
160 
161     /** Get an unmodifiable view of the sorted transitions.
162      * @return unmodifiable view of the sorted transitions
163      */
164     public SortedSet<Transition<T, D>> getTransitions() {
165         return Collections.unmodifiableSortedSet(data);
166     }
167 
168     /** Local class holding transition times. */
169     public static class Transition<S, D extends CalculusFieldElement<D>> implements TimeStamped {
170 
171         /** Transition date. */
172         private final FieldAbsoluteDate<D> date;
173 
174         /** Entry valid before the transition. */
175         private final S before;
176 
177         /** Entry valid after the transition. */
178         private final S after;
179 
180         /** Simple constructor.
181          * @param date transition date
182          * @param before entry valid before the transition
183          * @param after entry valid after the transition
184          */
185         private Transition(final FieldAbsoluteDate<D> date, final S before, final S after) {
186             this.date   = date;
187             this.before = before;
188             this.after  = after;
189         }
190 
191         /** Get the transition field absolute date.
192          * @return transition date
193          */
194         public FieldAbsoluteDate<D> getAbsoluteDate() {
195             return date;
196         }
197         /** Get the transition absolute date.
198          * @return transition date
199          */
200 
201         public AbsoluteDate getDate() {
202             return date.toAbsoluteDate();
203         }
204         /** Get the entry valid before transition.
205          * @return entry valid before transition
206          */
207         public S getBefore() {
208             return before;
209         }
210 
211         /** Get the entry valid after transition.
212          * @return entry valid after transition
213          */
214         public S getAfter() {
215             return after;
216         }
217 
218     }
219 
220 
221 
222 }