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