package au.gov.ga.earthsci.model.data;
import java.nio.ByteBuffer;
import java.util.UUID;
import javax.measure.unit.Unit;
import au.gov.ga.earthsci.common.buffer.BufferType;
import au.gov.ga.earthsci.common.util.Validate;
/**
* A general-purpose immutable {@link IModelData} implementation backed by a
* {@link ByteBuffer}
* <p/>
* Client code should use the {@link #getBufferType()} method to determine what
* type of values to read from the buffer.
* <p/>
* The {@link #getSource()} method of this implementation will return a
* duplicate view of the underlying source buffer. This allows multiple threads
* to modify buffer limits and positions etc. safely. Note, however, that for
* performance reasons the returned buffers share the same underlying data.
* <b>No modifications should be made to the data obtained from
* {@link #getSource()}</b>. To do so is considered programmer error.
*
* @author James Navin (james.navin@ga.gov.au)
*/
public class ByteBufferModelData implements IModelData
{
private final ByteBuffer buffer;
private final BufferType type;
private final String id;
private final String name;
private final String description;
private final Object nodata;
private final Unit<?> units;
private final int numValues;
private final int groupSize;
private final int numGroups;
/**
* Create a new model data instance with group size of 1 and no name,
* description or units etc.
*
* @param buffer
* The buffer containing the data (required)
* @param type
* The type of value contained in the data (required)
*/
public ByteBufferModelData(ByteBuffer buffer, BufferType type)
{
this(null, null, null, buffer, type, null, null);
}
/**
* Create a new model data instance with a group size of 1.
*
* @param id
* The ID to assign the data (if <code>null</code> will
* auto-generate a unique ID)
* @param name
* The (localised) human-readable name for the data (optional but
* recommended)
* @param description
* The (localised) human-readable description of the data
* (optional)
* @param buffer
* The buffer containing the data (required)
* @param type
* The type of value contained in the buffer (required)
* @param nodata
* The nodata value for this data (optional)
* @param units
* Units associated with this data (optional)
*/
public ByteBufferModelData(String id, String name, String description,
ByteBuffer buffer, BufferType type,
Object nodata, Unit<?> units)
{
this(id, name, description, buffer, type, 1, nodata, units);
}
/**
* Create a new fully configured model data instance
*
* @param id
* The ID to assign the data (if <code>null</code> will
* auto-generate a unique ID)
* @param name
* The (localised) human-readable name for the data (optional but
* recommended)
* @param description
* The (localised) human-readable description of the data
* (optional)
* @param buffer
* The buffer containing the data (required)
* @param type
* The type of value contained in the buffer (required)
* @param groupSize
* The (positive integer) size of value groups contained in the
* buffer. A size of 1 indicates no grouping. (required)
* @param nodata
* The nodata value for this data (optional)
* @param units
* Units associated with this data (optional)
*/
public ByteBufferModelData(String id, String name, String description,
ByteBuffer buffer, BufferType type, int groupSize,
Object nodata, Unit<?> units)
{
Validate.notNull(buffer, "A byte buffer is required"); //$NON-NLS-1$
Validate.notNull(type, "A buffer type is required"); //$NON-NLS-1$
Validate.isTrue(groupSize > 0, "Group size must be a positive integer"); //$NON-NLS-1$
if (nodata != null)
{
Validate.isTrue(type.isAssignableFrom(nodata), "NODATA must be of type " + type.name() //$NON-NLS-1$
+ ", not " + nodata.getClass().getSimpleName()); //$NON-NLS-1$
}
this.id = id == null ? UUID.randomUUID().toString() : id;
this.name = name;
this.description = description;
this.buffer = buffer;
this.type = type;
this.nodata = nodata;
this.units = units;
// Pre-compute values to avoid computation during render loops etc.
// Done here rather than lazily to allow use of final variables.
this.numValues = buffer.limit() / type.getNumberOfBytes();
this.groupSize = groupSize;
this.numGroups = numValues / groupSize;
}
@Override
public String getId()
{
return id;
}
@Override
public String getName()
{
return name;
}
@Override
public String getDescription()
{
return description;
}
@Override
public Object getNoDataValue()
{
return nodata;
}
@Override
public ByteBuffer getSource()
{
ByteBuffer result = (ByteBuffer) buffer.duplicate().rewind();
result.order(buffer.order());
return result;
}
@Override
public BufferType getBufferType()
{
return type;
}
@Override
public Unit<?> getUnits()
{
return units;
}
@Override
public boolean hasUnits()
{
return units != null;
}
@Override
public int getNumberOfValues()
{
return numValues;
}
@Override
public int getGroupSize()
{
return groupSize;
}
@Override
public int getNumberOfGroups()
{
return numGroups;
}
private String stringRepresentation;
@SuppressWarnings("nls")
@Override
public String toString()
{
if (stringRepresentation != null)
{
return stringRepresentation;
}
StringBuffer result = new StringBuffer();
result.append("ByteBufferModelData [").append('\n');
result.append(" ").append("ID: ").append(id).append('\n');
result.append(" ").append("Name: ").append(name).append('\n');
result.append(" ").append("Description: ").append(description).append('\n');
result.append(" ").append("Type: ").append(type).append('\n');
result.append(" ").append("NumValues: ").append(numValues).append('\n');
result.append(" ").append("NumGroups: ").append(numGroups).append('\n');
result.append(" ").append("GroupSize: ").append(groupSize).append('\n');
result.append(" ").append("NODATA: ").append(nodata).append('\n');
result.append("]");
stringRepresentation = result.toString();
return stringRepresentation;
}
}