1 /* Copyright 2002-2025 Bryan Cazabonne
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 * Bryan Cazabonne 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 org.hipparchus.CalculusFieldElement;
20 import org.hipparchus.Field;
21
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28
29 /** String → Object mapping, for small number of keys.
30 * <p>
31 * This class is a low overhead for a very small number of keys.
32 * It is based on simple array and string comparison. It plays
33 * the same role a {@code Map<String, Object>} but with reduced
34 * features and not intended for large number of keys. For such
35 * needs the regular {@code Map<String, Object>} should be preferred.
36 * </p>
37 *
38 * @see FieldArrayDictionary
39 * @author Bryan Cazabonne
40 * @since 13.0
41 */
42 public class FieldDataDictionary<T extends CalculusFieldElement<T>> {
43
44 /** Default capacity. */
45 private static final int DEFAULT_INITIAL_CAPACITY = 4;
46
47 /** Field to which elements belong. */
48 private final Field<T> field;
49
50 /** Data container. */
51 private final List<Entry> data;
52
53 /** Constructor with {@link #DEFAULT_INITIAL_CAPACITY default initial capacity}.
54 * @param field field to which elements belong
55 */
56 public FieldDataDictionary(final Field<T> field) {
57 this(field, DEFAULT_INITIAL_CAPACITY);
58 }
59
60 /** Constructor from a map.
61 * @param field field to which the elements belong
62 * @param map map to use for initializing entries
63 */
64 public FieldDataDictionary(final Field<T> field, final Map<String, Object> map) {
65 this(field, map.size());
66 for (final Map.Entry<String, Object> entry : map.entrySet()) {
67 // we don't call put(key, value) to avoid the overhead of the unneeded call to remove(key)
68 data.add(new Entry(entry.getKey(), entry.getValue()));
69 }
70 }
71
72 /** Constructor from another dictionary.
73 * @param dictionary dictionary to use for initializing entries
74 */
75 public FieldDataDictionary(final FieldDataDictionary<T> dictionary) {
76 // take care to call dictionary.getData() and not use dictionary.data
77 this(dictionary.getField(), DEFAULT_INITIAL_CAPACITY + dictionary.getData().size());
78 for (final Entry entry : dictionary.getData()) {
79 // we don't call put(key, value) to avoid the overhead of the unneeded call to remove(key)
80 data.add(new Entry(entry.getKey(), entry.getValue()));
81 }
82 }
83
84 /** Constructor with specified capacity.
85 * @param field field to which elements belong
86 * @param initialCapacity initial capacity
87 */
88 public FieldDataDictionary(final Field<T> field, final int initialCapacity) {
89 this.data = new ArrayList<>(initialCapacity);
90 this.field = field;
91 }
92
93 /** Creates a "field" values dictionary.
94 * <p>
95 * Creates a DoubleArrayDictionary with all double[] values
96 * contained in the instance.
97 * </p>
98 * @return a double values dictionary
99 */
100 @SuppressWarnings("unchecked") // cast including generic type is checked and unitary tested
101 public FieldArrayDictionary<T> toFieldArrayDictionary() {
102 final FieldArrayDictionary<T> dictionary = new FieldArrayDictionary<>(field);
103 for (final Entry entry : data) {
104 if (entry.getValue() instanceof CalculusFieldElement[]) {
105 dictionary.put(entry.getKey(), (T[]) entry.getValue());
106 }
107 }
108 return dictionary;
109 }
110
111 /** Create a map from the instance.
112 * <p>
113 * The map contains a copy of the instance data
114 * </p>
115 * @return copy of the dictionary, as an independent map
116 */
117 public Map<String, Object> toMap() {
118 final Map<String, Object> map = new HashMap<>(data.size());
119 for (final Entry entry : data) {
120 map.put(entry.getKey(), entry.getValue());
121 }
122 return map;
123 }
124
125 /** Remove all entries.
126 */
127 public void clear() {
128 data.clear();
129 }
130
131 /** Get the number of dictionary entries.
132 * @return number of dictionary entries
133 */
134 public int size() {
135 return data.size();
136 }
137
138 /** Add an entry.
139 * <p>
140 * If an entry with the same key already exists, it will be removed first.
141 * </p>
142 * <p>
143 * The new entry is always put at the end.
144 * </p>
145 * @param key entry key
146 * @param value entry value
147 */
148 public void put(final String key, final Object value) {
149 remove(key);
150 data.add(new Entry(key, value));
151 }
152
153 /** Put all the T[] entries from the map in the dictionary.
154 * @param map map to copy into the instance
155 */
156 public void putAllFields(final Map<String, T[]> map) {
157 for (final Map.Entry<String, T[]> entry : map.entrySet()) {
158 put(entry.getKey(), entry.getValue());
159 }
160 }
161
162
163 /** Put all the entries from the map in the dictionary.
164 * @param map map to copy into the instance
165 */
166 public void putAll(final Map<String, Object> map) {
167 for (final Map.Entry<String, Object> entry : map.entrySet()) {
168 put(entry.getKey(), entry.getValue());
169 }
170 }
171
172 /** Put all the entries from another dictionary.
173 * @param dictionary dictionary to copy into the instance
174 */
175 public void putAll(final FieldDataDictionary<T> dictionary) {
176 for (final FieldDataDictionary<T>.Entry entry : dictionary.data) {
177 put(entry.getKey(), entry.getValue());
178 }
179 }
180
181 /** Get the value corresponding to a key.
182 * @param key entry key
183 * @return copy of the value corresponding to the key or null if key not present
184 */
185 public Object get(final String key) {
186 final FieldDataDictionary<T>.Entry entry = getEntry(key);
187 return entry == null ? null : entry.getValue();
188 }
189
190 /** Get a complete entry.
191 * @param key entry key
192 * @return entry with key if it exists, null otherwise
193 */
194 public Entry getEntry(final String key) {
195 for (final Entry entry : data) {
196 if (entry.getKey().equals(key)) {
197 return entry;
198 }
199 }
200 return null;
201 }
202
203 /** Remove an entry.
204 * @param key key of the entry to remove
205 * @return true if an entry has been removed, false if the key was not present
206 */
207 public boolean remove(final String key) {
208 final Iterator<Entry> iterator = data.iterator();
209 while (iterator.hasNext()) {
210 if (iterator.next().getKey().equals(key)) {
211 iterator.remove();
212 return true;
213 }
214 }
215 return false;
216 }
217
218 /**
219 * Get an unmodifiable view of the dictionary entries.
220 *
221 * @return unmodifiable view of the dictionary entries
222 */
223 public List<Entry> getData() {
224 return Collections.unmodifiableList(data);
225 }
226
227 /**
228 * Get the field to which elements belong.
229 * @return the field to which elements belong
230 */
231 public Field<T> getField() {
232 return field;
233 }
234
235 /** Get a string representation of the dictionary.
236 * <p>
237 * This string representation is intended for improving displays in debuggers only.
238 * </p>
239 * @return string representation of the dictionary
240 */
241 @Override
242 public String toString() {
243 return DataDictionary.toString(toMap());
244 }
245
246 /** Entry in a dictionary. */
247 public class Entry {
248
249 /** Key. */
250 private final String key;
251
252 /** Value. */
253 private final Object value;
254
255 /** Simple constructor.
256 * @param key key
257 * @param value value
258 */
259 Entry(final String key, final Object value) {
260 this.key = key;
261 this.value = value;
262 }
263
264 /** Get the entry key.
265 * @return entry key
266 */
267 public String getKey() {
268 return key;
269 }
270
271 /** Get the value.
272 * @return a copy of the value (independent from internal array if it is a double array)
273 */
274 @SuppressWarnings("unchecked")
275 public Object getValue() {
276 return value instanceof CalculusFieldElement[] ? ((T[]) value).clone() : value;
277 }
278
279 /** Increment the value with another scaled entry.
280 * <p>
281 * Each component {@code value[i]} will be replaced by {@code value[i] + factor * raw.value[i]}.
282 * </p>
283 * <p>
284 * For the sake of performance, no checks are done on arguments.
285 * </p>
286 * @param factor multiplicative factor for increment
287 * @param raw raw increment to be multiplied by {@code factor} and then added
288 */
289 @SuppressWarnings("unchecked")
290 public void scaledIncrement(final double factor, final FieldArrayDictionary<T>.Entry raw) {
291 if (value instanceof CalculusFieldElement[]) {
292 for (int i = 0; i < raw.getValue().length; ++i) {
293 ((T[]) value)[i] = ((T[]) value)[i].add(raw.getValue()[i].multiply(factor));
294 }
295 }
296 }
297
298 /** Increment the value with another scaled entry.
299 * <p>
300 * Each component {@code value[i]} will be replaced by {@code value[i] + factor * raw.value[i]}.
301 * </p>
302 * <p>
303 * For the sake of performance, no checks are done on arguments.
304 * </p>
305 * @param factor multiplicative factor for increment
306 * @param raw raw increment to be multiplied by {@code factor} and then added
307 */
308 @SuppressWarnings("unchecked")
309 public void scaledIncrement(final T factor, final FieldArrayDictionary<T>.Entry raw) {
310 if (value instanceof CalculusFieldElement[]) {
311 for (int i = 0; i < raw.getValue().length; ++i) {
312 ((T[]) value)[i] = ((T[]) value)[i].add(raw.getValue()[i].multiply(factor));
313 }
314 }
315 }
316
317 }
318
319 }