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.ArrayList;
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.List;
23  import java.util.stream.Stream;
24  
25  import org.hipparchus.CalculusFieldElement;
26  import org.hipparchus.exception.LocalizedCoreFormats;
27  import org.orekit.errors.OrekitException;
28  import org.orekit.errors.OrekitIllegalArgumentException;
29  import org.orekit.errors.OrekitIllegalStateException;
30  import org.orekit.errors.OrekitMessages;
31  import org.orekit.errors.TimeStampedCacheException;
32  import org.orekit.time.FieldAbsoluteDate;
33  import org.orekit.time.FieldChronologicalComparator;
34  import org.orekit.time.FieldTimeStamped;
35  import org.orekit.time.TimeStamped;
36  
37  /**
38   * A cache of {@link TimeStamped} data that provides concurrency through immutability. This strategy is suitable when all the
39   * cached data is stored in memory. (For example, {@link org.orekit.time.UTCScale UTCScale}) This class then provides
40   * convenient methods for accessing the data.
41   *
42   * @param <T> the type of data
43   * @param <KK> the type the field element
44   *
45   * @author Evan Ward
46   * @author Vincent Cucchietti
47   */
48  public class ImmutableFieldTimeStampedCache<T extends FieldTimeStamped<KK>, KK extends CalculusFieldElement<KK>>
49          implements FieldTimeStampedCache<T, KK> {
50  
51      /** An empty immutable cache that always throws an exception on attempted access.
52       * @since 12.1
53       */
54      @SuppressWarnings("rawtypes")
55      private static final ImmutableFieldTimeStampedCache EMPTY_CACHE =
56          new EmptyFieldTimeStampedCache();
57  
58      /**
59       * the cached data. Be careful not to modify it after the constructor, or return a reference that allows mutating this
60       * list.
61       */
62      private final List<T> data;
63  
64      /** the maximum size list to return from {@link #getNeighbors(FieldAbsoluteDate)}. */
65      private final int maxNeighborsSize;
66  
67      /**
68       * Create a new cache with the given neighbors size and data.
69       *
70       * @param maxNeighborsSize the maximum size of the list returned from {@link #getNeighbors(FieldAbsoluteDate)}. Must be less than or
71       * equal to {@code data.size()}.
72       * @param data the backing data for this cache. The list will be copied to ensure immutability. To guarantee immutability
73       * the entries in {@code data} must be immutable themselves. There must be more data than {@code maxNeighborsSize}.
74       *
75       * @throws IllegalArgumentException if {@code maxNeighborsSize > data.size()} or if {@code maxNeighborsSize} is negative
76       */
77      public ImmutableFieldTimeStampedCache(final int maxNeighborsSize,
78                                            final Collection<? extends T> data) {
79          // Parameter check
80          if (maxNeighborsSize > data.size()) {
81              throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_CACHED_NEIGHBORS,
82                                                       data.size(), maxNeighborsSize);
83          }
84          if (maxNeighborsSize < 1) {
85              throw new OrekitIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL,
86                                                       maxNeighborsSize, 1);
87          }
88  
89          // Assign instance variables
90          this.maxNeighborsSize = maxNeighborsSize;
91  
92          // Sort and copy data first
93          this.data = new ArrayList<>(data);
94          this.data.sort(new FieldChronologicalComparator<>());
95  
96      }
97  
98      /** Private constructor for {@link #EMPTY_CACHE}.
99       */
100     private ImmutableFieldTimeStampedCache() {
101         this.data             = null;
102         this.maxNeighborsSize = 0;
103     }
104 
105     /**
106      * Get an empty immutable cache.
107      *
108      * @param <TS> the type of data
109      * @param <CFE> the type of the calculus field element
110      * @return an empty {@link ImmutableTimeStampedCache}.
111      * @since 12.1
112      */
113     @SuppressWarnings("unchecked")
114     public static <TS extends FieldTimeStamped<CFE>, CFE extends CalculusFieldElement<CFE>>
115         ImmutableFieldTimeStampedCache<TS, CFE> emptyCache() {
116         return (ImmutableFieldTimeStampedCache<TS, CFE>) EMPTY_CACHE;
117     }
118 
119     /** {@inheritDoc} */
120     public Stream<T> getNeighbors(final FieldAbsoluteDate<KK> central, final int n) {
121         if (n > maxNeighborsSize) {
122             throw new OrekitException(OrekitMessages.NOT_ENOUGH_DATA, maxNeighborsSize);
123         }
124         return new FieldSortedListTrimmer(n).getNeighborsSubList(central, data).stream();
125     }
126 
127     /** {@inheritDoc} */
128     public int getMaxNeighborsSize() {
129         return this.maxNeighborsSize;
130     }
131 
132     /** {@inheritDoc} */
133     public T getEarliest() {
134         return this.data.get(0);
135     }
136 
137     /** {@inheritDoc} */
138     public T getLatest() {
139         return this.data.get(this.data.size() - 1);
140     }
141 
142     /**
143      * Get all the data in this cache.
144      *
145      * @return a sorted collection of all data passed in the
146      * {@link #ImmutableFieldTimeStampedCache(int, Collection) constructor}.
147      */
148     public List<T> getAll() {
149         return Collections.unmodifiableList(this.data);
150     }
151 
152     /** {@inheritDoc} */
153     @Override
154     public String toString() {
155         return "Immutable cache with " + this.data.size() + " entries";
156     }
157 
158     /** An empty immutable cache that always throws an exception on attempted access. */
159     private static class EmptyFieldTimeStampedCache<T extends FieldTimeStamped<KK>, KK extends CalculusFieldElement<KK>>
160             extends ImmutableFieldTimeStampedCache<T, KK> {
161 
162         /** {@inheritDoc} */
163         @Override
164         public Stream<T> getNeighbors(final FieldAbsoluteDate<KK> central) {
165             throw new TimeStampedCacheException(OrekitMessages.NO_CACHED_ENTRIES);
166         }
167 
168         /** {@inheritDoc} */
169         @Override
170         public int getMaxNeighborsSize() {
171             return 0;
172         }
173 
174         /** {@inheritDoc} */
175         @Override
176         public T getEarliest() {
177             throw new OrekitIllegalStateException(OrekitMessages.NO_CACHED_ENTRIES);
178         }
179 
180         /** {@inheritDoc} */
181         @Override
182         public T getLatest() {
183             throw new OrekitIllegalStateException(OrekitMessages.NO_CACHED_ENTRIES);
184         }
185 
186         /** {@inheritDoc} */
187         @Override
188         public List<T> getAll() {
189             return Collections.emptyList();
190         }
191 
192         /** {@inheritDoc} */
193         @Override
194         public String toString() {
195             return "Empty immutable cache";
196         }
197 
198     }
199 
200 }