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 }