ImmutableFieldTimeStampedCache.java
/* Copyright 2002-2024 CS GROUP
* Licensed to CS GROUP (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import org.hipparchus.CalculusFieldElement;
import org.hipparchus.Field;
import org.hipparchus.exception.LocalizedCoreFormats;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitIllegalArgumentException;
import org.orekit.errors.OrekitIllegalStateException;
import org.orekit.errors.OrekitMessages;
import org.orekit.errors.TimeStampedCacheException;
import org.orekit.time.FieldAbsoluteDate;
import org.orekit.time.FieldChronologicalComparator;
import org.orekit.time.FieldTimeStamped;
import org.orekit.time.TimeStamped;
/**
* A cache of {@link TimeStamped} data that provides concurrency through immutability. This strategy is suitable when all the
* cached data is stored in memory. (For example, {@link org.orekit.time.UTCScale UTCScale}) This class then provides
* convenient methods for accessing the data.
*
* @param <T> the type of data
* @param <KK> the type the field element
*
* @author Evan Ward
* @author Vincent Cucchietti
*/
public class ImmutableFieldTimeStampedCache<T extends FieldTimeStamped<KK>, KK extends CalculusFieldElement<KK>>
implements FieldTimeStampedCache<T, KK> {
/** An empty immutable cache that always throws an exception on attempted access.
* @since 12.1
*/
@SuppressWarnings("rawtypes")
private static final ImmutableFieldTimeStampedCache EMPTY_CACHE =
new EmptyFieldTimeStampedCache();
/**
* the cached data. Be careful not to modify it after the constructor, or return a reference that allows mutating this
* list.
*/
private final List<T> data;
/** the maximum size list to return from {@link #getNeighbors(FieldAbsoluteDate)}. */
private final int maxNeighborsSize;
/**
* Create a new cache with the given neighbors size and data.
*
* @param maxNeighborsSize the maximum size of the list returned from {@link #getNeighbors(FieldAbsoluteDate)}. Must be less than or
* equal to {@code data.size()}.
* @param data the backing data for this cache. The list will be copied to ensure immutability. To guarantee immutability
* the entries in {@code data} must be immutable themselves. There must be more data than {@code maxNeighborsSize}.
*
* @throws IllegalArgumentException if {@code maxNeighborsSize > data.size()} or if {@code maxNeighborsSize} is negative
*/
public ImmutableFieldTimeStampedCache(final int maxNeighborsSize,
final Collection<? extends T> data) {
// Parameter check
if (maxNeighborsSize > data.size()) {
throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_CACHED_NEIGHBORS,
data.size(), maxNeighborsSize);
}
if (maxNeighborsSize < 1) {
throw new OrekitIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL,
maxNeighborsSize, 1);
}
// Assign instance variables
this.maxNeighborsSize = maxNeighborsSize;
// Sort and copy data first
this.data = new ArrayList<>(data);
this.data.sort(new FieldChronologicalComparator<>());
}
/** Private constructor for {@link #EMPTY_CACHE}.
*/
private ImmutableFieldTimeStampedCache() {
this.data = null;
this.maxNeighborsSize = 0;
}
/**
* Get an empty immutable cache, cast to the correct type.
*
* @param <TS> the type of data
* @param <CFE> the type of the calculus field element
* @param ignored field to which the elements belong
* @return an empty {@link ImmutableTimeStampedCache}.
* @deprecated as of 12.1, replaced by {@link #emptyCache()}
*/
@Deprecated
public static <TS extends FieldTimeStamped<CFE>, CFE extends CalculusFieldElement<CFE>>
ImmutableFieldTimeStampedCache<TS, CFE> emptyCache(final Field<CFE> ignored) {
return emptyCache();
}
/**
* Get an empty immutable cache.
*
* @param <TS> the type of data
* @param <CFE> the type of the calculus field element
* @return an empty {@link ImmutableTimeStampedCache}.
* @since 12.1
*/
@SuppressWarnings("unchecked")
public static <TS extends FieldTimeStamped<CFE>, CFE extends CalculusFieldElement<CFE>>
ImmutableFieldTimeStampedCache<TS, CFE> emptyCache() {
return (ImmutableFieldTimeStampedCache<TS, CFE>) EMPTY_CACHE;
}
/** {@inheritDoc} */
public Stream<T> getNeighbors(final FieldAbsoluteDate<KK> central, final int n) {
if (n > maxNeighborsSize) {
throw new OrekitException(OrekitMessages.NOT_ENOUGH_DATA, maxNeighborsSize);
}
return new FieldSortedListTrimmer(n).getNeighborsSubList(central, data).stream();
}
/** {@inheritDoc} */
public int getMaxNeighborsSize() {
return this.maxNeighborsSize;
}
/** {@inheritDoc} */
public T getEarliest() {
return this.data.get(0);
}
/** {@inheritDoc} */
public T getLatest() {
return this.data.get(this.data.size() - 1);
}
/**
* Get all the data in this cache.
*
* @return a sorted collection of all data passed in the
* {@link #ImmutableFieldTimeStampedCache(int, Collection) constructor}.
*/
public List<T> getAll() {
return Collections.unmodifiableList(this.data);
}
/** {@inheritDoc} */
@Override
public String toString() {
return "Immutable cache with " + this.data.size() + " entries";
}
/** An empty immutable cache that always throws an exception on attempted access. */
private static class EmptyFieldTimeStampedCache<T extends FieldTimeStamped<KK>, KK extends CalculusFieldElement<KK>>
extends ImmutableFieldTimeStampedCache<T, KK> {
/** {@inheritDoc} */
@Override
public Stream<T> getNeighbors(final FieldAbsoluteDate<KK> central) {
throw new TimeStampedCacheException(OrekitMessages.NO_CACHED_ENTRIES);
}
/** {@inheritDoc} */
@Override
public int getMaxNeighborsSize() {
return 0;
}
/** {@inheritDoc} */
@Override
public T getEarliest() {
throw new OrekitIllegalStateException(OrekitMessages.NO_CACHED_ENTRIES);
}
/** {@inheritDoc} */
@Override
public T getLatest() {
throw new OrekitIllegalStateException(OrekitMessages.NO_CACHED_ENTRIES);
}
/** {@inheritDoc} */
@Override
public List<T> getAll() {
return Collections.emptyList();
}
/** {@inheritDoc} */
@Override
public String toString() {
return "Empty immutable cache";
}
}
}