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