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 }