IndexedValue.java
package org.djunits.value;
import org.djunits.unit.Unit;
import org.djunits.value.base.Scalar;
import org.djunits.value.storage.Storage;
import org.djunits.value.storage.StorageType;
import org.djutils.exceptions.Throw;
/**
* AbstractIndexedValue.java.
* <p>
* Copyright (c) 2019-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
* BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
* </p>
* @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
* @param <U> the unit type
* @param <S> the scalar type for the U unit
* @param <T> the value type for this unit
* @param <D> the data storage type
*/
public abstract class IndexedValue<U extends Unit<U>, S extends Scalar<U, S>,
T extends IndexedValue<U, S, T, D>, D extends Storage<D>> implements Value<U, T>
{
/** */
private static final long serialVersionUID = 20190927L;
/** The display unit of this AbstractValue. */
private U displayUnit;
/** If set, any modification of the data must be preceded by replacing the data with a local copy. */
private boolean copyOnWrite = false;
/** helper flag to indicate whether the data is mutable or not. */
private boolean mutable = false;
/**
* Construct a new IndexedValue.
* @param displayUnit U; the unit of the new AbstractValue
*/
public IndexedValue(final U displayUnit)
{
Throw.whenNull(displayUnit, "display unit cannot be null");
this.displayUnit = displayUnit;
}
@Override
public final U getDisplayUnit()
{
return this.displayUnit;
}
@Override
public void setDisplayUnit(final U newUnit)
{
Throw.whenNull(newUnit, "newUnit may not be null");
this.displayUnit = newUnit;
}
/**
* Retrieve the data object. Method can only be used within package and by subclasses.
* @return D; the internal data
*/
protected abstract D getData();
/**
* Set the data object. Method can only be used within package and by subclasses.
* @param data D; the internal data
*/
protected abstract void setData(D data);
/**
* Return the StorageType (DENSE, SPARSE, etc.) for the stored vector.
* @return StorageType; the storage type (DENSE, SPARSE, etc.) for the stored vector
*/
public final StorageType getStorageType()
{
return getData().getStorageType();
}
/**
* Check the copyOnWrite flag and, if it is set, make a deep copy of the data and clear the flag.
* @throws ValueRuntimeException if the vector is immutable
*/
protected final void checkCopyOnWrite()
{
Throw.when(!this.mutable, ValueRuntimeException.class, "Immutable Vector cannot be modified");
if (isCopyOnWrite())
{
setData(getData().copy());
setCopyOnWrite(false);
}
}
/**
* Retrieve the value of the copyOnWrite flag.
* @return boolean
*/
protected final boolean isCopyOnWrite()
{
return this.copyOnWrite;
}
/**
* Change the copyOnWrite flag.
* @param copyOnWrite boolean; the new value for the copyOnWrite flag
*/
protected final void setCopyOnWrite(final boolean copyOnWrite)
{
this.copyOnWrite = copyOnWrite;
}
/**
* Return whether the data is mutable or not.
* @return boolean; whether the data is mutable or not
*/
public final boolean isMutable()
{
return this.mutable;
}
/**
* Set helper flag to indicate whether the data is mutable or not.
* @param mutable boolean; helper flag to indicate whether the data is mutable or not
*/
protected final void setMutable(final boolean mutable)
{
this.mutable = mutable;
}
/**
* Turn the immutable flag on for this vector.
* @return T; the vector with a raised immutable flag
*/
public final T immutable()
{
if (isMutable())
{
setCopyOnWrite(true);
}
T result = clone();
result.setCopyOnWrite(false);
result.setMutable(false);
return result;
}
/**
* Turn the immutable flag off for this internal storage.
* @return T; the internal storage with a cleared immutable flag
*/
public final T mutable()
{
if (isMutable())
{
setCopyOnWrite(true);
}
T result = clone();
result.setCopyOnWrite(true);
result.setMutable(true);
return result;
}
/**
* Return whether the internal storage type of the indexed value is dense or not.
* @return boolean; whether the internal storage type of the indexed value is dense or not
*/
public final boolean isDense()
{
return getData().isDense();
}
/**
* Return whether the internal storage type of the indexed value is sparse or not.
* @return boolean; whether the internal storage type of the indexed value is sparse or not
*/
public final boolean isSparse()
{
return getData().isSparse();
}
/**
* Count the number of cells that have a non-zero SI value.
* @return int; the number of cells having non-zero SI value
*/
public final int cardinality()
{
return getData().cardinality();
}
/**
* Create and return a dense version of this internal storage. When the data was already dense, the current version is
* returned and no copy will be made of the data.
* @return T; a dense version of this internal storage
*/
public abstract T toDense();
/**
* Create and return a sparse version of this internal storage. When the data was already sparse, the current version is
* returned and no copy will be made of the data.
* @return T; a sparse version of this internal storage
*/
public abstract T toSparse();
/**
* Return the class of the corresponding scalar.
* @return Class<S>; the class of the corresponding scalar
*/
public abstract Class<S> getScalarClass();
@SuppressWarnings("unchecked")
@Override
public T clone()
{
try
{
T result = (T) super.clone();
result.setData(getData().copy());
result.setCopyOnWrite(false);
return result;
}
catch (CloneNotSupportedException exception)
{
throw new RuntimeException(exception);
}
}
}