package mil.nga.giat.geowave.datastore.accumulo.query;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.iterators.Filter;
import org.apache.accumulo.core.iterators.IteratorEnvironment;
import org.apache.accumulo.core.iterators.SortedKeyValueIterator;
import org.apache.accumulo.core.iterators.user.WholeRowIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.io.BaseEncoding;
import mil.nga.giat.geowave.core.index.ByteArrayId;
import mil.nga.giat.geowave.core.index.ByteArrayUtils;
public class SingleEntryFilterIterator extends
Filter
{
private final static Logger LOGGER = LoggerFactory.getLogger(SingleEntryFilterIterator.class);
public static final String ENTRY_FILTER_ITERATOR_NAME = "GEOWAVE_ENTRY_FILTER_ITERATOR";
public static final int ENTRY_FILTER_ITERATOR_PRIORITY = 25;
public static final String WHOLE_ROW_ITERATOR_NAME = "GEOWAVE_WHOLE_ROW_ITERATOR";
public static final int WHOLE_ROW_ITERATOR_PRIORITY = ENTRY_FILTER_ITERATOR_PRIORITY - 1;
public static final String ADAPTER_ID = "adapterid";
public static final String DATA_IDS = "dataids";
public static final String WHOLE_ROW_ENCODED_KEY = "wholerow";
private boolean wholeRowEncoded;
private byte[] adapterId;
private List<byte[]> dataIds;
@Override
public boolean accept(
final Key k,
final Value v ) {
boolean accept = true;
Map<Key, Value> entries = null;
if (wholeRowEncoded) {
try {
entries = WholeRowIterator.decodeRow(
k,
v);
}
catch (final IOException e) {
LOGGER.error(
"Unable to decode row.",
e);
return false;
}
}
else {
entries = new HashMap<Key, Value>();
entries.put(
k,
v);
}
if ((entries != null) && entries.isEmpty()) {
accept = false;
}
else {
if (entries == null) {
LOGGER.error("Internal error in iterator - entries map null when it shouldn't be");
return false;
}
for (final Key key : entries.keySet()) {
final byte[] localAdapterId = key.getColumnFamilyData().getBackingArray();
if (Arrays.equals(
localAdapterId,
adapterId)) {
final byte[] accumRowId = key.getRowData().getBackingArray();
final byte[] metadata = Arrays.copyOfRange(
accumRowId,
accumRowId.length - 12,
accumRowId.length);
final ByteBuffer metadataBuf = ByteBuffer.wrap(metadata);
final int adapterIdLength = metadataBuf.getInt();
final int dataIdLength = metadataBuf.getInt();
final ByteBuffer buf = ByteBuffer.wrap(
accumRowId,
0,
accumRowId.length - 12);
final byte[] indexId = new byte[accumRowId.length - 12 - adapterIdLength - dataIdLength];
final byte[] rawAdapterId = new byte[adapterIdLength];
final byte[] rawDataId = new byte[dataIdLength];
buf.get(indexId);
buf.get(rawAdapterId);
buf.get(rawDataId);
accept = false;
for (final byte[] dataId : dataIds) {
if (Arrays.equals(
rawDataId,
dataId) && Arrays.equals(
rawAdapterId,
adapterId)) {
accept |= true;
}
}
}
else {
accept = false;
}
}
}
return accept;
}
public static final String encodeIDs(
final List<ByteArrayId> dataIds ) {
int size = 4;
for (final ByteArrayId id : dataIds) {
size += id.getBytes().length + 4;
}
final ByteBuffer buffer = ByteBuffer.allocate(size);
buffer.putInt(dataIds.size());
for (final ByteArrayId id : dataIds) {
final byte[] sId = id.getBytes();
buffer.putInt(sId.length);
buffer.put(sId);
}
return ByteArrayUtils.byteArrayToString(buffer.array());
}
private static final List<byte[]> decodeIDs(
final String dataIdsString ) {
final ByteBuffer buf = ByteBuffer.wrap(ByteArrayUtils.byteArrayFromString(dataIdsString));
final List<byte[]> list = new ArrayList<byte[]>();
int count = buf.getInt();
while (count > 0) {
final byte[] tempByte = new byte[buf.getInt()];
buf.get(tempByte);
list.add(tempByte);
count--;
}
return list;
}
@Override
public void init(
final SortedKeyValueIterator<Key, Value> source,
final Map<String, String> options,
final IteratorEnvironment env )
throws IOException {
final String adapterIdStr = options.get(ADAPTER_ID);
final String dataIdsStr = options.get(DATA_IDS);
if (adapterIdStr == null) {
throw new IllegalArgumentException(
"'adapterid' must be set for " + SingleEntryFilterIterator.class.getName());
}
if (dataIdsStr == null) {
throw new IllegalArgumentException(
"'dataid' must be set for " + SingleEntryFilterIterator.class.getName());
}
adapterId = BaseEncoding.base64Url().decode(
adapterIdStr);
dataIds = decodeIDs(dataIdsStr);
final String wholeRowEncodedStr = options.get(WHOLE_ROW_ENCODED_KEY);
// default to whole row encoded if not specified
wholeRowEncoded = (wholeRowEncodedStr == null || !wholeRowEncodedStr.equals(Boolean.toString(false)));
super.init(
source,
options,
env);
}
}