package mil.nga.giat.geowave.adapter.raster.adapter.merge;
import java.awt.image.SampleModel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.media.jai.remote.SerializableState;
import javax.media.jai.remote.SerializerFactory;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import mil.nga.giat.geowave.adapter.raster.adapter.RasterDataAdapter;
import mil.nga.giat.geowave.adapter.raster.adapter.RasterTile;
import mil.nga.giat.geowave.core.index.ByteArrayId;
import mil.nga.giat.geowave.core.index.Mergeable;
import mil.nga.giat.geowave.core.index.Persistable;
import mil.nga.giat.geowave.core.index.PersistenceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.opengis.coverage.grid.GridCoverage;
/**
*
*
* @param <T>
*/
public class RootMergeStrategy<T extends Persistable> implements
Mergeable
{
private final static Logger LOGGER = LoggerFactory.getLogger(RootMergeStrategy.class);
// the purpose for these maps instead of a list of samplemodel and adapter
// ID pairs is to allow for multiple adapters to share the same sample model
protected Map<Integer, SampleModel> sampleModels = new HashMap<Integer, SampleModel>();
public Map<ByteArrayId, Integer> adapterIdToSampleModelKey = new HashMap<ByteArrayId, Integer>();
public Map<Integer, RasterTileMergeStrategy<T>> childMergeStrategies = new HashMap<Integer, RasterTileMergeStrategy<T>>();
public Map<ByteArrayId, Integer> adapterIdToChildMergeStrategyKey = new HashMap<ByteArrayId, Integer>();
protected RootMergeStrategy() {}
public RootMergeStrategy(
final ByteArrayId adapterId,
final SampleModel sampleModel,
final RasterTileMergeStrategy<T> mergeStrategy ) {
sampleModels.put(
0,
sampleModel);
adapterIdToSampleModelKey.put(
adapterId,
0);
childMergeStrategies.put(
0,
mergeStrategy);
adapterIdToChildMergeStrategyKey.put(
adapterId,
0);
}
public SampleModel getSampleModel(
final ByteArrayId adapterId ) {
synchronized (this) {
final Integer sampleModelId = adapterIdToSampleModelKey.get(adapterId);
if (sampleModelId != null) {
return sampleModels.get(sampleModelId);
}
return null;
}
}
public RasterTileMergeStrategy<T> getChildMergeStrategy(
final ByteArrayId adapterId ) {
synchronized (this) {
final Integer childMergeStrategyId = adapterIdToChildMergeStrategyKey.get(adapterId);
if (childMergeStrategyId != null) {
return childMergeStrategies.get(childMergeStrategyId);
}
return null;
}
}
@Override
public void merge(
final Mergeable merge ) {
synchronized (this) {
if ((merge != null) && (merge instanceof RootMergeStrategy)) {
final RootMergeStrategy<T> other = (RootMergeStrategy) merge;
mergeMaps(
sampleModels,
adapterIdToSampleModelKey,
other.sampleModels,
other.adapterIdToSampleModelKey);
mergeMaps(
childMergeStrategies,
adapterIdToChildMergeStrategyKey,
other.childMergeStrategies,
other.adapterIdToChildMergeStrategyKey);
}
}
}
private static <T> void mergeMaps(
final Map<Integer, T> thisValues,
final Map<ByteArrayId, Integer> thisAdapterIdToValueKeys,
final Map<Integer, T> otherValues,
final Map<ByteArrayId, Integer> otherAdapterIdToValueKeys ) {
// this was generalized to apply to both sample models and merge
// strategies, comments refer to sample models but in general it is also
// applied to merge strategies
// first check for sample models that exist in 'other' that do
// not exist in 'this'
for (final Entry<Integer, T> sampleModelEntry : otherValues.entrySet()) {
if (!thisValues.containsValue(sampleModelEntry.getValue())) {
// we need to add this sample model
final List<ByteArrayId> adapterIds = new ArrayList<ByteArrayId>();
// find all adapter IDs associated with this sample
// model
for (final Entry<ByteArrayId, Integer> adapterIdEntry : otherAdapterIdToValueKeys.entrySet()) {
if (adapterIdEntry.getValue().equals(
sampleModelEntry.getKey())) {
adapterIds.add(adapterIdEntry.getKey());
}
}
if (!adapterIds.isEmpty()) {
addValue(
adapterIds,
sampleModelEntry.getValue(),
thisValues,
thisAdapterIdToValueKeys);
}
}
}
// next check for adapter IDs that exist in 'other' that do not
// exist in 'this'
for (final Entry<ByteArrayId, Integer> adapterIdEntry : otherAdapterIdToValueKeys.entrySet()) {
if (!thisAdapterIdToValueKeys.containsKey(adapterIdEntry.getKey())) {
// find the sample model associated with the adapter ID
// in 'other' and find what Integer it is with in 'this'
final T sampleModel = otherValues.get(adapterIdEntry.getValue());
if (sampleModel != null) {
// because the previous step added any missing
// sample models, it should be a fair assumption
// that the sample model exists in 'this'
for (final Entry<Integer, T> sampleModelEntry : thisValues.entrySet()) {
if (sampleModel.equals(sampleModelEntry.getValue())) {
// add the sample model key to the
// adapterIdToSampleModelKey map
thisAdapterIdToValueKeys.put(
adapterIdEntry.getKey(),
sampleModelEntry.getKey());
break;
}
}
}
}
}
}
private static synchronized <T> void addValue(
final List<ByteArrayId> adapterIds,
final T sampleModel,
final Map<Integer, T> values,
final Map<ByteArrayId, Integer> adapterIdToValueKeys ) {
int nextId = 1;
boolean idAvailable = false;
while (!idAvailable) {
boolean idMatched = false;
for (final Integer id : values.keySet()) {
if (nextId == id.intValue()) {
idMatched = true;
break;
}
}
if (idMatched) {
// try the next incremental ID
nextId++;
}
else {
// its not matched so we can use it
idAvailable = true;
}
}
values.put(
nextId,
sampleModel);
for (final ByteArrayId adapterId : adapterIds) {
adapterIdToValueKeys.put(
adapterId,
nextId);
}
}
@SuppressFBWarnings(value = {
"DLS_DEAD_LOCAL_STORE"
}, justification = "Incorrect warning, sampleModelBinary used")
@Override
public byte[] toBinary() {
int byteCount = 16;
final List<byte[]> sampleModelBinaries = new ArrayList<byte[]>();
final List<Integer> sampleModelKeys = new ArrayList<Integer>();
int successfullySerializedModels = 0;
int successfullySerializedModelAdapters = 0;
final Set<Integer> successfullySerializedModelIds = new HashSet<Integer>();
for (final Entry<Integer, SampleModel> entry : sampleModels.entrySet()) {
final SampleModel sampleModel = entry.getValue();
final SerializableState serializableSampleModel = SerializerFactory.getState(sampleModel);
byte[] sampleModelBinary = new byte[0];
try {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ObjectOutputStream oos = new ObjectOutputStream(
baos);
oos.writeObject(serializableSampleModel);
oos.close();
sampleModelBinary = baos.toByteArray();
byteCount += sampleModelBinary.length;
byteCount += 8;
sampleModelBinaries.add(sampleModelBinary);
sampleModelKeys.add(entry.getKey());
successfullySerializedModels++;
successfullySerializedModelIds.add(entry.getKey());
}
catch (final IOException e) {
LOGGER.warn(
"Unable to serialize sample model",
e);
}
}
for (final Entry<ByteArrayId, Integer> entry : adapterIdToSampleModelKey.entrySet()) {
if (successfullySerializedModelIds.contains(entry.getValue())) {
final byte[] keyBytes = entry.getKey().getBytes();
byteCount += keyBytes.length + 8;
successfullySerializedModelAdapters++;
}
}
final List<byte[]> mergeStrategyBinaries = new ArrayList<byte[]>();
final List<Integer> mergeStrategyKeys = new ArrayList<Integer>();
int successfullySerializedMergeStrategies = 0;
int successfullySerializedMergeAdapters = 0;
final Set<Integer> successfullySerializedMergeIds = new HashSet<Integer>();
for (final Entry<Integer, RasterTileMergeStrategy<T>> entry : childMergeStrategies.entrySet()) {
final RasterTileMergeStrategy<T> mergeStrategy = entry.getValue();
final byte[] mergeStrategyBinary = PersistenceUtils.toBinary(mergeStrategy);
byteCount += mergeStrategyBinary.length;
byteCount += 8;
mergeStrategyBinaries.add(mergeStrategyBinary);
mergeStrategyKeys.add(entry.getKey());
successfullySerializedMergeStrategies++;
successfullySerializedMergeIds.add(entry.getKey());
}
for (final Entry<ByteArrayId, Integer> entry : adapterIdToChildMergeStrategyKey.entrySet()) {
if (successfullySerializedMergeIds.contains(entry.getValue())) {
final byte[] keyBytes = entry.getKey().getBytes();
byteCount += keyBytes.length + 8;
successfullySerializedMergeAdapters++;
}
}
final ByteBuffer buf = ByteBuffer.allocate(byteCount);
buf.putInt(successfullySerializedModels);
for (int i = 0; i < successfullySerializedModels; i++) {
final byte[] sampleModelBinary = sampleModelBinaries.get(i);
buf.putInt(sampleModelBinary.length);
buf.put(sampleModelBinary);
buf.putInt(sampleModelKeys.get(i));
}
buf.putInt(successfullySerializedModelAdapters);
for (final Entry<ByteArrayId, Integer> entry : adapterIdToSampleModelKey.entrySet()) {
if (successfullySerializedModelIds.contains(entry.getValue())) {
final byte[] keyBytes = entry.getKey().getBytes();
buf.putInt(keyBytes.length);
buf.put(keyBytes);
buf.putInt(entry.getValue());
}
}
buf.putInt(successfullySerializedMergeStrategies);
for (int i = 0; i < successfullySerializedMergeStrategies; i++) {
final byte[] mergeStrategyBinary = mergeStrategyBinaries.get(i);
buf.putInt(mergeStrategyBinary.length);
buf.put(mergeStrategyBinary);
buf.putInt(mergeStrategyKeys.get(i));
}
buf.putInt(successfullySerializedMergeAdapters);
for (final Entry<ByteArrayId, Integer> entry : adapterIdToChildMergeStrategyKey.entrySet()) {
if (successfullySerializedModelIds.contains(entry.getValue())) {
final byte[] keyBytes = entry.getKey().getBytes();
buf.putInt(keyBytes.length);
buf.put(keyBytes);
buf.putInt(entry.getValue());
}
}
return buf.array();
}
@Override
public void fromBinary(
final byte[] bytes ) {
final ByteBuffer buf = ByteBuffer.wrap(bytes);
final int sampleModelSize = buf.getInt();
sampleModels = new HashMap<Integer, SampleModel>(
sampleModelSize);
for (int i = 0; i < sampleModelSize; i++) {
final byte[] sampleModelBinary = new byte[buf.getInt()];
if (sampleModelBinary.length > 0) {
try {
buf.get(sampleModelBinary);
final ByteArrayInputStream bais = new ByteArrayInputStream(
sampleModelBinary);
final ObjectInputStream ois = new ObjectInputStream(
bais);
final Object o = ois.readObject();
ois.close();
final int sampleModelKey = buf.getInt();
if ((o instanceof SerializableState)
&& (((SerializableState) o).getObject() instanceof SampleModel)) {
final SampleModel sampleModel = (SampleModel) ((SerializableState) o).getObject();
sampleModels.put(
sampleModelKey,
sampleModel);
}
}
catch (final Exception e) {
LOGGER.warn(
"Unable to deserialize sample model",
e);
}
}
else {
LOGGER.warn("Sample model binary is empty, unable to deserialize");
}
}
final int sampleModelAdapterIdSize = buf.getInt();
adapterIdToSampleModelKey = new HashMap<ByteArrayId, Integer>(
sampleModelAdapterIdSize);
for (int i = 0; i < sampleModelAdapterIdSize; i++) {
final int keyLength = buf.getInt();
final byte[] keyBytes = new byte[keyLength];
buf.get(keyBytes);
adapterIdToSampleModelKey.put(
new ByteArrayId(
keyBytes),
buf.getInt());
}
final int mergeStrategySize = buf.getInt();
childMergeStrategies = new HashMap<Integer, RasterTileMergeStrategy<T>>(
mergeStrategySize);
for (int i = 0; i < mergeStrategySize; i++) {
final byte[] mergeStrategyBinary = new byte[buf.getInt()];
if (mergeStrategyBinary.length > 0) {
try {
buf.get(mergeStrategyBinary);
final RasterTileMergeStrategy mergeStrategy = PersistenceUtils.fromBinary(
mergeStrategyBinary,
RasterTileMergeStrategy.class);
final int mergeStrategyKey = buf.getInt();
if (mergeStrategy != null) {
childMergeStrategies.put(
mergeStrategyKey,
mergeStrategy);
}
}
catch (final Exception e) {
LOGGER.warn(
"Unable to deserialize merge strategy",
e);
}
}
else {
LOGGER.warn("Merge strategy binary is empty, unable to deserialize");
}
}
final int mergeStrategyAdapterIdSize = buf.getInt();
adapterIdToChildMergeStrategyKey = new HashMap<ByteArrayId, Integer>(
mergeStrategyAdapterIdSize);
for (int i = 0; i < mergeStrategyAdapterIdSize; i++) {
final int keyLength = buf.getInt();
final byte[] keyBytes = new byte[keyLength];
buf.get(keyBytes);
adapterIdToChildMergeStrategyKey.put(
new ByteArrayId(
keyBytes),
buf.getInt());
}
}
public void merge(
final RasterTile<T> thisTile,
final RasterTile<T> nextTile,
final ByteArrayId dataAdapterId ) {
final RasterTileMergeStrategy<T> childMergeStrategy = getChildMergeStrategy(dataAdapterId);
if (childMergeStrategy != null) {
childMergeStrategy.merge(
thisTile,
nextTile,
getSampleModel(dataAdapterId));
}
}
public T getMetadata(
final GridCoverage tileGridCoverage,
final Map originalCoverageProperties,
final RasterDataAdapter dataAdapter ) {
final RasterTileMergeStrategy<T> childMergeStrategy = getChildMergeStrategy(dataAdapter.getAdapterId());
if (childMergeStrategy != null) {
return childMergeStrategy.getMetadata(
tileGridCoverage,
dataAdapter);
}
return null;
}
}