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