View Javadoc
1   package org.djunits.value;
2   
3   import org.djunits.unit.Unit;
4   import org.djunits.value.base.Scalar;
5   import org.djunits.value.storage.Storage;
6   import org.djunits.value.storage.StorageType;
7   import org.djutils.exceptions.Throw;
8   
9   /**
10   * AbstractIndexedValue.java.
11   * <p>
12   * Copyright (c) 2019-2025 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
13   * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
14   * </p>
15   * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
16   * @param <U> the unit type
17   * @param <S> the scalar type for the U unit
18   * @param <T> the value type for this unit
19   * @param <D> the data storage type
20   */
21  public abstract class IndexedValue<U extends Unit<U>, S extends Scalar<U, S>, T extends IndexedValue<U, S, T, D>,
22          D extends Storage<D>> implements Value<U, T>
23  {
24      /** */
25      private static final long serialVersionUID = 20190927L;
26  
27      /** The display unit of this AbstractValue. */
28      private U displayUnit;
29  
30      /** If set, any modification of the data must be preceded by replacing the data with a local copy. */
31      private boolean copyOnWrite = false;
32  
33      /** helper flag to indicate whether the data is mutable or not. */
34      private boolean mutable = false;
35  
36      /**
37       * Construct a new IndexedValue.
38       * @param displayUnit the unit of the new AbstractValue
39       */
40      public IndexedValue(final U displayUnit)
41      {
42          Throw.whenNull(displayUnit, "display unit cannot be null");
43          this.displayUnit = displayUnit;
44      }
45  
46      @Override
47      public final U getDisplayUnit()
48      {
49          return this.displayUnit;
50      }
51  
52      @SuppressWarnings("unchecked")
53      @Override
54      public T setDisplayUnit(final U newUnit)
55      {
56          Throw.whenNull(newUnit, "newUnit may not be null");
57          this.displayUnit = newUnit;
58          return (T) this;
59      }
60  
61      /**
62       * Retrieve the data object. Method can only be used within package and by subclasses.
63       * @return the internal data
64       */
65      protected abstract D getData();
66  
67      /**
68       * Set the data object. Method can only be used within package and by subclasses.
69       * @param data the internal data
70       */
71      protected abstract void setData(D data);
72  
73      /**
74       * Return the StorageType (DENSE, SPARSE, etc.) for the stored vector.
75       * @return the storage type (DENSE, SPARSE, etc.) for the stored vector
76       */
77      public final StorageType getStorageType()
78      {
79          return getData().getStorageType();
80      }
81  
82      /**
83       * Check the copyOnWrite flag and, if it is set, make a deep copy of the data and clear the flag.
84       * @throws ValueRuntimeException if the vector is immutable
85       */
86      protected final void checkCopyOnWrite()
87      {
88          Throw.when(!this.mutable, ValueRuntimeException.class, "Immutable Vector cannot be modified");
89          if (isCopyOnWrite())
90          {
91              setData(getData().copy());
92              setCopyOnWrite(false);
93          }
94      }
95  
96      /**
97       * Retrieve the value of the copyOnWrite flag.
98       * @return boolean
99       */
100     protected final boolean isCopyOnWrite()
101     {
102         return this.copyOnWrite;
103     }
104 
105     /**
106      * Change the copyOnWrite flag.
107      * @param copyOnWrite the new value for the copyOnWrite flag
108      */
109     protected final void setCopyOnWrite(final boolean copyOnWrite)
110     {
111         this.copyOnWrite = copyOnWrite;
112     }
113 
114     /**
115      * Return whether the data is mutable or not.
116      * @return whether the data is mutable or not
117      */
118     public final boolean isMutable()
119     {
120         return this.mutable;
121     }
122 
123     /**
124      * Set helper flag to indicate whether the data is mutable or not.
125      * @param mutable helper flag to indicate whether the data is mutable or not
126      */
127     protected final void setMutable(final boolean mutable)
128     {
129         this.mutable = mutable;
130     }
131 
132     /**
133      * Turn the immutable flag on for this vector.
134      * @return the vector with a raised immutable flag
135      */
136     public final T immutable()
137     {
138         if (isMutable())
139         {
140             setCopyOnWrite(true);
141         }
142         T result = clone();
143         result.setCopyOnWrite(false);
144         result.setMutable(false);
145         return result;
146     }
147 
148     /**
149      * Turn the immutable flag off for this internal storage.
150      * @return the internal storage with a cleared immutable flag
151      */
152     public final T mutable()
153     {
154         if (isMutable())
155         {
156             setCopyOnWrite(true);
157         }
158         T result = clone();
159         result.setCopyOnWrite(true);
160         result.setMutable(true);
161         return result;
162     }
163 
164     /**
165      * Return whether the internal storage type of the indexed value is dense or not.
166      * @return whether the internal storage type of the indexed value is dense or not
167      */
168     public final boolean isDense()
169     {
170         return getData().isDense();
171     }
172 
173     /**
174      * Return whether the internal storage type of the indexed value is sparse or not.
175      * @return whether the internal storage type of the indexed value is sparse or not
176      */
177     public final boolean isSparse()
178     {
179         return getData().isSparse();
180     }
181 
182     /**
183      * Count the number of cells that have a non-zero SI value.
184      * @return the number of cells having non-zero SI value
185      */
186     public final int cardinality()
187     {
188         return getData().cardinality();
189     }
190 
191     /**
192      * Create and return a dense version of this internal storage. When the data was already dense, the current version is
193      * returned and no copy will be made of the data.
194      * @return a dense version of this internal storage
195      */
196     public abstract T toDense();
197 
198     /**
199      * Create and return a sparse version of this internal storage. When the data was already sparse, the current version is
200      * returned and no copy will be made of the data.
201      * @return a sparse version of this internal storage
202      */
203     public abstract T toSparse();
204 
205     /**
206      * Return the class of the corresponding scalar.
207      * @return the class of the corresponding scalar
208      */
209     public abstract Class<S> getScalarClass();
210 
211     @SuppressWarnings("unchecked")
212     @Override
213     public T clone()
214     {
215         try
216         {
217             T result = (T) super.clone();
218             result.setData(getData().copy());
219             result.setCopyOnWrite(false);
220             return result;
221         }
222         catch (CloneNotSupportedException exception)
223         {
224             throw new RuntimeException(exception);
225         }
226     }
227 
228 }