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