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 }