DataDictionary.java

  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. import java.io.Serializable;
  19. import java.util.ArrayList;
  20. import java.util.Arrays;
  21. import java.util.Collections;
  22. import java.util.HashMap;
  23. import java.util.Iterator;
  24. import java.util.List;
  25. import java.util.Map;

  26. /** String → Object mapping, for small number of keys.
  27.  * <p>
  28.  * This class is a low overhead for a very small number of keys.
  29.  * It is based on simple array and string comparison. It plays
  30.  * the same role a {@code Map<String, Object>} but with reduced
  31.  * features and not intended for large number of keys. For such
  32.  * needs the regular {@code Map<String, Object>} should be preferred.
  33.  * </p>
  34.  *
  35.  * @see DoubleArrayDictionary
  36.  * @author Anne-Laure Lugan
  37.  * @since 13.0
  38.  */
  39. public class DataDictionary implements Serializable {

  40.     /** Serializable UID. */
  41.     private static final long serialVersionUID = 20250208L;

  42.     /** Default capacity. */
  43.     private static final int DEFAULT_INITIAL_CAPACITY = 4;

  44.     /** Data container. */
  45.     private final List<Entry> data;

  46.     /** Constructor with {@link #DEFAULT_INITIAL_CAPACITY default initial capacity}.
  47.      */
  48.     public DataDictionary() {
  49.         this(DEFAULT_INITIAL_CAPACITY);
  50.     }

  51.     /** Constructor with specified capacity.
  52.      * @param initialCapacity initial capacity
  53.      */
  54.     public DataDictionary(final int initialCapacity) {
  55.         data = new ArrayList<>(initialCapacity);
  56.     }

  57.     /** Constructor from another dictionary.
  58.      * @param dictionary dictionary to use for initializing entries
  59.      */
  60.     public DataDictionary(final DataDictionary dictionary) {
  61.         // take care to call dictionary.getData() and not use dictionary.data,
  62.         // otherwise we get an empty dictionary when using a DoubleArrayDictionary.view
  63.         this(DEFAULT_INITIAL_CAPACITY + dictionary.getData().size());
  64.         for (final Entry entry : dictionary.getData()) {
  65.             // we don't call put(key, value) to avoid the overhead of the unneeded call to remove(key)
  66.             data.add(new Entry(entry.getKey(), entry.getValue()));
  67.         }
  68.     }

  69.     /** Creates a double values dictionary.
  70.      * <p>
  71.      * Creates a DoubleArrayDictionary with all double[] values
  72.      * contained in the instance.
  73.      * </p>
  74.      * @return a double values dictionary
  75.      */
  76.     public DoubleArrayDictionary toDoubleDictionary() {
  77.         final DoubleArrayDictionary dictionary = new DoubleArrayDictionary();
  78.         for (final Entry entry : data) {
  79.             if (entry.getValue() instanceof double[]) {
  80.                 dictionary.put(entry.getKey(), (double[]) entry.getValue());
  81.             }
  82.         }
  83.         return dictionary;
  84.     }

  85.     /**
  86.      * Get an unmodifiable view of the dictionary entries.
  87.      *
  88.      * @return unmodifiable view of the dictionary entries
  89.      */
  90.     public List<Entry> getData() {
  91.         return Collections.unmodifiableList(data);
  92.     }

  93.     /** Get the number of dictionary entries.
  94.      * @return number of dictionary entries
  95.      */
  96.     public int size() {
  97.         return data.size();
  98.     }

  99.     /** Create a map from the instance.
  100.      * <p>
  101.      * The map contains a copy of the instance data
  102.      * </p>
  103.      * @return copy of the dictionary, as an independent map
  104.      */
  105.     public Map<String, Object> toMap() {
  106.         final Map<String, Object> map = new HashMap<>(data.size());
  107.         for (final Entry entry : data) {
  108.             map.put(entry.getKey(), entry.getValue());
  109.         }
  110.         return map;
  111.     }

  112.     /** Remove all entries.
  113.      */
  114.     public void clear() {
  115.         data.clear();
  116.     }

  117.     /** Add an entry.
  118.      * <p>
  119.      * If an entry with the same key already exists, it will be removed first.
  120.      * </p>
  121.      * <p>
  122.      * The new entry is always put at the end.
  123.      * </p>
  124.      * @param key entry key
  125.      * @param value entry value
  126.      */
  127.     public void put(final String key, final Object value) {
  128.         remove(key);
  129.         data.add(new Entry(key, value));
  130.     }

  131.     /** Put all the double[] entries from the map in the dictionary.
  132.      * @param map map to copy into the instance
  133.      */
  134.     public void putAllDoubles(final Map<String, double[]> map) {
  135.         for (final Map.Entry<String,  double[]> entry : map.entrySet()) {
  136.             put(entry.getKey(), entry.getValue());
  137.         }
  138.     }

  139.     /** Put all the entries from another dictionary.
  140.      * @param dictionary dictionary to copy into the instance
  141.      */
  142.     public void putAll(final DataDictionary dictionary) {
  143.         for (final Entry entry : dictionary.data) {
  144.             put(entry.getKey(), entry.getValue());
  145.         }
  146.     }

  147.     /** Get the value corresponding to a key.
  148.      * @param key entry key
  149.      * @return copy of the value corresponding to the key or null if key not present
  150.      */
  151.     public Object get(final String key) {
  152.         final Entry entry = getEntry(key);
  153.         return entry == null ? null : entry.getValue();
  154.     }


  155.     /** Get a complete entry.
  156.      * @param key entry key
  157.      * @return entry with key if it exists, null otherwise
  158.      */
  159.     public Entry getEntry(final String key) {
  160.         for (final Entry entry : data) {
  161.             if (entry.getKey().equals(key)) {
  162.                 return entry;
  163.             }
  164.         }
  165.         return null;
  166.     }

  167.     /** Remove an entry.
  168.      * @param key key of the entry to remove
  169.      * @return true if an entry has been removed, false if the key was not present
  170.      */
  171.     public boolean remove(final String key) {
  172.         final Iterator<Entry> iterator = data.iterator();
  173.         while (iterator.hasNext()) {
  174.             if (iterator.next().getKey().equals(key)) {
  175.                 iterator.remove();
  176.                 return true;
  177.             }
  178.         }
  179.         return false;
  180.     }

  181.     /** Get a string representation of the dictionary.
  182.      * <p>
  183.      * This string representation is intended for improving displays in debuggers only.
  184.      * </p>
  185.      * @param values dictionary
  186.      * @return string representation of the dictionary
  187.      */
  188.     public static String toString(final Map<String, Object> values) {
  189.         final StringBuilder builder = new StringBuilder();
  190.         builder.append('{');
  191.         int i = 0;
  192.         for (Map.Entry<String, Object> entry : values.entrySet()) {
  193.             if (i > 0) {
  194.                 builder.append(", ");
  195.             }
  196.             builder.append(entry.getKey());
  197.             builder.append('[');
  198.             if (entry.getValue() instanceof double[]) {
  199.                 builder.append(((double[]) entry.getValue()).length);
  200.             } else {
  201.                 builder.append(entry.getValue());
  202.             }
  203.             builder.append(']');
  204.             i++;
  205.         }
  206.         builder.append('}');
  207.         return builder.toString();
  208.     }

  209.     /** Get a string representation of the dictionary.
  210.      * <p>
  211.      * This string representation is intended for improving displays in debuggers only.
  212.      * </p>
  213.      * @return string representation of the dictionary
  214.      */
  215.     @Override
  216.     public String toString() {
  217.         return toString(toMap());
  218.     }

  219.     /** Entry in a dictionary. */
  220.     public static class Entry implements Serializable {

  221.         /** Serializable UID. */
  222.         private static final long serialVersionUID = 20250208L;

  223.         /** Key. */
  224.         private final String key;

  225.         /** Value. */
  226.         private final Object value;

  227.         /** Simple constructor.
  228.          * @param key key
  229.          * @param value value
  230.          */
  231.         Entry(final String key, final Object value) {
  232.             this.key   = key;
  233.             this.value = value;
  234.         }

  235.         /** Get the entry key.
  236.          * @return entry key
  237.          */
  238.         public String getKey() {
  239.             return key;
  240.         }

  241.         /** Get the value.
  242.          * @return a copy of the value (independent from internal array if it is a double array)
  243.          */
  244.         public Object getValue() {
  245.             return value instanceof double[] ? ((double[]) value).clone() : value;
  246.         }

  247.         /** Increment the value if it is a double array.
  248.          * <p>
  249.          * For the sake of performance, no checks are done on argument.
  250.          * </p>
  251.          * @param increment increment to apply to the entry value
  252.          */
  253.         public void increment(final double[] increment) {
  254.             if (value instanceof double[]) {
  255.                 for (int i = 0; i < increment.length; ++i) {
  256.                     ((double[]) value)[i] += increment[i];
  257.                 }
  258.             }
  259.         }

  260.         /** Increment the value with another scaled entry if it is a double array.
  261.          * <p>
  262.          * Each component {@code value[i]} will be replaced by {@code value[i] + factor * raw.value[i]}.
  263.          * </p>
  264.          * <p>
  265.          * For the sake of performance, no checks are done on arguments.
  266.          * </p>
  267.          * @param factor multiplicative factor for increment
  268.          * @param raw raw increment to be multiplied by {@code factor} and then added
  269.          */
  270.         public void scaledIncrement(final double factor, final DoubleArrayDictionary.Entry raw) {
  271.             if (value instanceof double[]) {
  272.                 for (int i = 0; i < raw.getValue().length; ++i) {
  273.                     ((double[]) value)[i] += factor * raw.getValue()[i];
  274.                 }
  275.             }
  276.         }

  277.         /** Reset the value to zero if it is a double array.
  278.          */
  279.         public void zero() {
  280.             if (value instanceof double[]) {
  281.                 Arrays.fill((double[]) value, 0.0);
  282.             }
  283.         }
  284.     }
  285. }