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