package mil.nga.giat.geowave.adapter.vector;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.geotools.data.DataUtilities;
import org.geotools.feature.SchemaException;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.referencing.CRS;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.MathTransform;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import mil.nga.giat.geowave.adapter.vector.index.SecondaryIndexManager;
import mil.nga.giat.geowave.adapter.vector.plugin.GeoWaveGTDataStore;
import mil.nga.giat.geowave.adapter.vector.plugin.visibility.VisibilityConfiguration;
import mil.nga.giat.geowave.adapter.vector.stats.StatsConfigurationCollection.SimpleFeatureStatsConfigurationCollection;
import mil.nga.giat.geowave.adapter.vector.stats.StatsManager;
import mil.nga.giat.geowave.adapter.vector.util.FeatureDataUtils;
import mil.nga.giat.geowave.adapter.vector.utils.SimpleFeatureUserDataConfigurationSet;
import mil.nga.giat.geowave.adapter.vector.utils.TimeDescriptors;
import mil.nga.giat.geowave.adapter.vector.utils.TimeDescriptors.TimeDescriptorConfiguration;
import mil.nga.giat.geowave.core.geotime.store.dimension.Time;
import mil.nga.giat.geowave.core.index.ByteArrayId;
import mil.nga.giat.geowave.core.index.PersistenceUtils;
import mil.nga.giat.geowave.core.index.StringUtils;
import mil.nga.giat.geowave.core.store.EntryVisibilityHandler;
import mil.nga.giat.geowave.core.store.adapter.AbstractDataAdapter;
import mil.nga.giat.geowave.core.store.adapter.AdapterPersistenceEncoding;
import mil.nga.giat.geowave.core.store.adapter.IndexFieldHandler;
import mil.nga.giat.geowave.core.store.adapter.NativeFieldHandler;
import mil.nga.giat.geowave.core.store.adapter.NativeFieldHandler.RowBuilder;
import mil.nga.giat.geowave.core.store.adapter.PersistentIndexFieldHandler;
import mil.nga.giat.geowave.core.store.adapter.statistics.DataStatistics;
import mil.nga.giat.geowave.core.store.adapter.statistics.StatisticsProvider;
import mil.nga.giat.geowave.core.store.data.field.FieldReader;
import mil.nga.giat.geowave.core.store.data.field.FieldUtils;
import mil.nga.giat.geowave.core.store.data.field.FieldVisibilityHandler;
import mil.nga.giat.geowave.core.store.data.field.FieldWriter;
import mil.nga.giat.geowave.core.store.data.visibility.VisibilityManagement;
import mil.nga.giat.geowave.core.store.dimension.NumericDimensionField;
import mil.nga.giat.geowave.core.store.index.CommonIndexModel;
import mil.nga.giat.geowave.core.store.index.CommonIndexValue;
import mil.nga.giat.geowave.core.store.index.SecondaryIndex;
import mil.nga.giat.geowave.core.store.index.SecondaryIndexDataAdapter;
import mil.nga.giat.geowave.mapreduce.HadoopDataAdapter;
import mil.nga.giat.geowave.mapreduce.HadoopWritableSerializer;
/**
* This data adapter will handle all reading/writing concerns for storing and
* retrieving GeoTools SimpleFeature objects to and from a GeoWave persistent
* store in Accumulo. <br>
* <br>
*
* If the implementor needs to write rows with particular visibility, this can
* be done by providing a FieldVisibilityHandler to a constructor or a
* VisibilityManagement to a constructor. When using VisibilityManagement, the
* feature attribute to contain the visibility meta-data is, by default, called
* GEOWAVE_VISIBILITY. This can be overridden by setting the UserData property
* 'visibility' to Boolean.TRUE for the feature attribute that describes the
* attribute that contains the visibility meta-data.
* persistedType.getDescriptor("someAttributeName").getUserData().put(
* "visibility", Boolean.TRUE)<br>
* <br>
* The adapter will use the SimpleFeature's default geometry for spatial
* indexing.<br>
* <br>
* The adaptor will use the first temporal attribute (a Calendar or Date object)
* as the timestamp of a temporal index.<br>
* <br>
* If the feature type contains a UserData property 'time' for a specific time
* attribute with Boolean.TRUE, then the attribute is used as the timestamp of a
* temporal index.<br>
* <br>
* If the feature type contains UserData properties 'start' and 'end' for two
* different time attributes with value Boolean.TRUE, then the attributes are
* used for a range index.<br>
* <br>
* If the feature type contains a UserData property 'time' for *all* time
* attributes with Boolean.FALSE, then a temporal index is not used.<br>
* <br>
* Statistics configurations are maintained in UserData. Each attribute may have
* a UserData property called 'stats'. The associated value is an instance of
* {@link mil.nga.giat.geowave.adapter.vector.stats.StatsConfigurationCollection}
* . The collection maintains a set of
* {@link mil.nga.giat.geowave.adapter.vector.stats.StatsConfig}, one for each
* type of statistic. The default statistics for geometry and temporal
* constraints cannot be changed, as they are critical components to the
* efficiency of query processing.
*
*/
public class FeatureDataAdapter extends
AbstractDataAdapter<SimpleFeature> implements
GeotoolsFeatureDataAdapter,
StatisticsProvider<SimpleFeature>,
HadoopDataAdapter<SimpleFeature, FeatureWritable>,
SecondaryIndexDataAdapter<SimpleFeature>
{
private final static Logger LOGGER = LoggerFactory.getLogger(FeatureDataAdapter.class);
// the original coordinate system will always be represented internally by
// the persisted type
private SimpleFeatureType persistedFeatureType;
// externally the reprojected type will always be advertised because all
// features will be reprojected to EPSG:4326 and the advertised feature type
// from the data adapter should match in CRS
private SimpleFeatureType reprojectedFeatureType;
private MathTransform transform;
private StatsManager statsManager;
private SecondaryIndexManager secondaryIndexManager;
private TimeDescriptors timeDescriptors = null;
// should change this anytime the serialized image changes. Stay negative.
// so 0xa0, 0xa1, 0xa2 etc.
final static byte VERSION = (byte) 0xa2;
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
protected FeatureDataAdapter() {}
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/**
* Constructor<br>
* Creates a FeatureDataAdapter for the specified SimpleFeatureType
*
* @param featureType
* - feature type for this object
*/
public FeatureDataAdapter(
final SimpleFeatureType featureType ) {
this(
featureType,
new ArrayList<PersistentIndexFieldHandler<SimpleFeature, ? extends CommonIndexValue, Object>>(),
null,
null);
}
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/**
* Constructor<br>
* Creates a FeatureDataAdapter for the specified SimpleFeatureType with the
* provided customIndexHandlers
*
* @param featureType
* - feature type for this object
* @param customIndexHandlers
* - l
*/
public FeatureDataAdapter(
final SimpleFeatureType featureType,
final List<PersistentIndexFieldHandler<SimpleFeature, ? extends CommonIndexValue, Object>> customIndexHandlers ) {
this(
featureType,
customIndexHandlers,
null,
null);
}
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/**
* Constructor<br>
* Creates a FeatureDataAdapter for the specified SimpleFeatureType with the
* provided visibilityManagement
*
* @param featureType
* - feature type for this object
* @param visibilityManagement
*/
public FeatureDataAdapter(
final SimpleFeatureType featureType,
final VisibilityManagement<SimpleFeature> visibilityManagement ) {
this(
featureType,
new ArrayList<PersistentIndexFieldHandler<SimpleFeature, ? extends CommonIndexValue, Object>>(),
null,
visibilityManagement);
}
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/**
* Constructor<br>
* Creates a FeatureDataAdapter for the specified SimpleFeatureType with the
* provided fieldVisiblityHandler
*
* @param featureType
* - feature type for this object
* @param fieldVisiblityHandler
*/
public FeatureDataAdapter(
final SimpleFeatureType featureType,
final FieldVisibilityHandler<SimpleFeature, Object> fieldVisiblityHandler ) {
this(
featureType,
new ArrayList<PersistentIndexFieldHandler<SimpleFeature, ? extends CommonIndexValue, Object>>(),
fieldVisiblityHandler,
null);
}
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/**
* Constructor<br>
* Creates a FeatureDataAdapter for the specified SimpleFeatureType with the
* provided customIndexHandlers, fieldVisibilityHandler and
* defaultVisibilityManagement
*
* @param featureType
* - feature type for this object
* @param customIndexHandlers
* @param fieldVisiblityHandler
* @param defaultVisibilityManagement
*/
public FeatureDataAdapter(
final SimpleFeatureType featureType,
final List<PersistentIndexFieldHandler<SimpleFeature, ? extends CommonIndexValue, Object>> customIndexHandlers,
final FieldVisibilityHandler<SimpleFeature, Object> fieldVisiblityHandler,
final VisibilityManagement<SimpleFeature> defaultVisibilityManagement ) {
super(
customIndexHandlers,
new ArrayList<NativeFieldHandler<SimpleFeature, Object>>(),
fieldVisiblityHandler,
updateVisibility(
featureType,
defaultVisibilityManagement));
setFeatureType(featureType);
}
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/**
* Helper method for establishing a visibility manager in the constructor
*/
private static SimpleFeatureType updateVisibility(
final SimpleFeatureType featureType,
final VisibilityManagement<SimpleFeature> defaultVisibilityManagement ) {
final VisibilityConfiguration config = new VisibilityConfiguration(
featureType);
config.updateWithDefaultIfNeeded(
featureType,
defaultVisibilityManagement);
return featureType;
}
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/**
* Set the FeatureType for this Data Adapter.
*
* @param featureType
* - new feature type
*/
private void setFeatureType(
final SimpleFeatureType featureType ) {
persistedFeatureType = featureType;
// If the CRS for the new FeatureType is the DEFAULT_CRS, then setup the
// reprojected type based on the persisted type
if (GeoWaveGTDataStore.DEFAULT_CRS.equals(featureType.getCoordinateReferenceSystem())) {
reprojectedFeatureType = persistedFeatureType;
}
else {
reprojectedFeatureType = SimpleFeatureTypeBuilder.retype(
featureType,
GeoWaveGTDataStore.DEFAULT_CRS);
if (featureType.getCoordinateReferenceSystem() != null) {
try {
transform = CRS.findMathTransform(
featureType.getCoordinateReferenceSystem(),
GeoWaveGTDataStore.DEFAULT_CRS,
true);
}
catch (final FactoryException e) {
LOGGER.warn(
"Unable to create coordinate reference system transform",
e);
}
}
}
resetTimeDescriptors();
statsManager = new StatsManager(
this,
persistedFeatureType,
reprojectedFeatureType,
transform);
secondaryIndexManager = new SecondaryIndexManager(
this,
persistedFeatureType,
statsManager);
}
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/**
* Create List of NativeFieldHandlers based on the SimpleFeature type passed
* as parameter.
*
* @param featureType
* - SFT to be used to determine handlers
*
* @return List of NativeFieldHandlers that correspond to attributes in
* featureType
*/
protected List<NativeFieldHandler<SimpleFeature, Object>> getFieldHandlersFromFeatureType(
final SimpleFeatureType featureType ) {
final List<NativeFieldHandler<SimpleFeature, Object>> nativeHandlers = new ArrayList<NativeFieldHandler<SimpleFeature, Object>>(
featureType.getAttributeCount());
for (final AttributeDescriptor attrDesc : featureType.getAttributeDescriptors()) {
nativeHandlers.add(new FeatureAttributeHandler(
attrDesc));
}
return nativeHandlers;
}
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/**
* Attempts to find a time descriptor (range or timestamp) within provided
* featureType and return index field handler for it.
*
* @param featureType
* - feature type to be scanned.
*
* @return Index Field Handler for the time descriptor found in featureType
*/
protected IndexFieldHandler<SimpleFeature, Time, Object> getTimeRangeHandler(
final SimpleFeatureType featureType ) {
final VisibilityConfiguration config = new VisibilityConfiguration(
featureType);
final TimeDescriptors timeDescriptors = inferTimeAttributeDescriptor(featureType);
if ((timeDescriptors.getStartRange() != null) && (timeDescriptors.getEndRange() != null)) {
FeatureAttributeHandler fah_startRange = new FeatureAttributeHandler(
timeDescriptors.getStartRange());
FeatureAttributeHandler fah_endRange = new FeatureAttributeHandler(
timeDescriptors.getEndRange());
FieldVisibilityHandler<SimpleFeature, Object> visibilityHandler = config
.getManager()
.createVisibilityHandler(
timeDescriptors.getStartRange().getLocalName(),
fieldVisiblityHandler,
config.getAttributeName());
FeatureTimeRangeHandler ftrh = new FeatureTimeRangeHandler(
fah_startRange,
fah_endRange,
visibilityHandler);
return (ftrh);
}
else if (timeDescriptors.getTime() != null) {
// if we didn't succeed in identifying a start and end time,
// just grab the first attribute and use it as a timestamp
FieldVisibilityHandler<SimpleFeature, Object> visibilityHandler = config
.getManager()
.createVisibilityHandler(
timeDescriptors.getTime().getLocalName(),
fieldVisiblityHandler,
config.getAttributeName());
FeatureTimestampHandler fth = new FeatureTimestampHandler(
timeDescriptors.getTime(),
visibilityHandler);
return fth;
}
return null;
}
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/**
* Get a List<> of the default index field handlers from the Simple Feature
* Type provided
*
* @param typeObj
* - Simple Feature Type object
* @return - List of the default Index Field Handlers
*/
@Override
protected List<IndexFieldHandler<SimpleFeature, ? extends CommonIndexValue, Object>> getDefaultTypeMatchingHandlers(
final Object typeObj ) {
if ((typeObj != null) && (typeObj instanceof SimpleFeatureType)) {
final SimpleFeatureType internalType = (SimpleFeatureType) typeObj;
final List<IndexFieldHandler<SimpleFeature, ? extends CommonIndexValue, Object>> defaultHandlers = new ArrayList<IndexFieldHandler<SimpleFeature, ? extends CommonIndexValue, Object>>();
nativeFieldHandlers = getFieldHandlersFromFeatureType(internalType);
// Add default handler for Time
final IndexFieldHandler<SimpleFeature, Time, Object> timeHandler = getTimeRangeHandler(internalType);
if (timeHandler != null) {
defaultHandlers.add(timeHandler);
}
// Add default handler for Geometry
final AttributeDescriptor descriptor = internalType.getGeometryDescriptor();
final VisibilityConfiguration visConfig = new VisibilityConfiguration(
internalType);
defaultHandlers.add(new FeatureGeometryHandler(
descriptor,
visConfig.getManager().createVisibilityHandler(
descriptor.getLocalName(),
fieldVisiblityHandler,
visConfig.getAttributeName())));
return defaultHandlers;
}
LOGGER.warn("Simple Feature Type could not be used for handling the indexed data");
return super.getDefaultTypeMatchingHandlers(reprojectedFeatureType);
}
/**
* Sets the namespace of the reprojected feature type associated with this
* data adapter
*
* @param namespaceURI
* - new namespace URI
*/
public void setNamespace(
final String namespaceURI ) {
final SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.init(reprojectedFeatureType);
builder.setNamespaceURI(namespaceURI);
reprojectedFeatureType = builder.buildFeatureType();
}
// ----------------------------------------------------------------------------------
/**
* Map of Field Readers associated with a Field ID
*/
private final Map<ByteArrayId, FieldReader<Object>> mapOfFieldIdToReaders = new HashMap<ByteArrayId, FieldReader<Object>>();
/**
* {@inheritDoc}
*
* @return Field Reader for the given Field ID
*/
@Override
public FieldReader<Object> getReader(
final ByteArrayId fieldId ) {
// Go to the map to get a reader for given fieldId
FieldReader<Object> reader = mapOfFieldIdToReaders.get(fieldId);
// Check the map to see if a reader has already been found.
if (reader == null) {
// Reader not in Map, go to the reprojected feature type and get the
// default reader
final AttributeDescriptor descriptor = reprojectedFeatureType.getDescriptor(fieldId.getString());
final Class<?> bindingClass = descriptor.getType().getBinding();
reader = (FieldReader<Object>) FieldUtils.getDefaultReaderForClass(bindingClass);
// Add it to map for the next time
mapOfFieldIdToReaders.put(
fieldId,
reader);
}
return reader;
}
// ----------------------------------------------------------------------------------
/**
* Map of Field Writers associated with a Field ID
*/
private final Map<ByteArrayId, FieldWriter<SimpleFeature, Object>> mapOfFieldIdToWriters = new HashMap<ByteArrayId, FieldWriter<SimpleFeature, Object>>();
/**
* {@inheritDoc}
*
* @return Field Writer for the given Field ID
*/
@Override
public FieldWriter<SimpleFeature, Object> getWriter(
final ByteArrayId fieldId ) {
// Go to the map to get a writer for given fieldId
FieldWriter<SimpleFeature, Object> writer = mapOfFieldIdToWriters.get(fieldId);
// Check the map to see if a writer has already been found.
if (writer == null) {
final FieldVisibilityHandler<SimpleFeature, Object> handler = getLocalVisibilityHandler(fieldId);
final AttributeDescriptor descriptor = reprojectedFeatureType.getDescriptor(fieldId.getString());
final Class<?> bindingClass = descriptor.getType().getBinding();
if (handler != null) {
writer = (FieldWriter<SimpleFeature, Object>) FieldUtils.getDefaultWriterForClass(
bindingClass,
handler);
}
else {
writer = (FieldWriter<SimpleFeature, Object>) FieldUtils.getDefaultWriterForClass(bindingClass);
}
if (writer == null) {
LOGGER.error("BasicWriter not found for binding type:" + bindingClass.getName().toString());
}
mapOfFieldIdToWriters.put(
fieldId,
writer);
}
return writer;
}
// ----------------------------------------------------------------------------------
private FieldVisibilityHandler<SimpleFeature, Object> getLocalVisibilityHandler(
final ByteArrayId fieldId ) {
final VisibilityConfiguration visConfig = new VisibilityConfiguration(
reprojectedFeatureType);
// See if there is a visibility config stored in the reprojected feature
// type
if (reprojectedFeatureType.getDescriptor(visConfig.getAttributeName()) == null) {
// No, so return the default field visibility handler
return fieldVisiblityHandler;
}
// Yes, then get the descriptor for the given field ID
final AttributeDescriptor descriptor = reprojectedFeatureType.getDescriptor(fieldId.getString());
return visConfig.getManager().createVisibilityHandler(
descriptor.getLocalName(),
fieldVisiblityHandler,
visConfig.getAttributeName());
}
// ----------------------------------------------------------------------------------
/**
* Get feature type default data contained in the reprojected SFT and
* serialize to a binary stream
*
* @return byte array with binary data
*/
@Override
protected byte[] defaultTypeDataToBinary() {
// serialize the persisted/reprojected feature type by using default
// fields and
// data types
final String encodedType = DataUtilities.encodeType(persistedFeatureType);
final String axis = FeatureDataUtils.getAxis(persistedFeatureType.getCoordinateReferenceSystem());
final String typeName = reprojectedFeatureType.getTypeName();
final byte[] typeNameBytes = StringUtils.stringToBinary(typeName);
final byte[] axisBytes = StringUtils.stringToBinary(axis);
byte[] attrBytes = new byte[0];
//
final SimpleFeatureUserDataConfigurationSet userDataConfiguration = new SimpleFeatureUserDataConfigurationSet();
userDataConfiguration.addConfigurations(
typeName,
new TimeDescriptorConfiguration(
persistedFeatureType));
userDataConfiguration.addConfigurations(
typeName,
new SimpleFeatureStatsConfigurationCollection(
persistedFeatureType));
userDataConfiguration.addConfigurations(
typeName,
new VisibilityConfiguration(
persistedFeatureType));
try {
attrBytes = StringUtils.stringToBinary(userDataConfiguration.asJsonString());
}
catch (final IOException e) {
LOGGER.error(
"Failure to encode simple feature user data configuration",
e);
}
final String namespace = reprojectedFeatureType.getName().getNamespaceURI();
byte[] namespaceBytes;
if ((namespace != null) && (namespace.length() > 0)) {
namespaceBytes = StringUtils.stringToBinary(namespace);
}
else {
namespaceBytes = new byte[0];
}
final byte[] encodedTypeBytes = StringUtils.stringToBinary(encodedType);
final byte[] secondaryIndexBytes = PersistenceUtils.toBinary(secondaryIndexManager);
// 21 bytes is the 7 four byte length fields and one byte for the
// version
final ByteBuffer buf = ByteBuffer.allocate(encodedTypeBytes.length + typeNameBytes.length
+ namespaceBytes.length + attrBytes.length + axisBytes.length + secondaryIndexBytes.length + 21);
buf.put(VERSION);
buf.putInt(typeNameBytes.length);
buf.putInt(namespaceBytes.length);
buf.putInt(attrBytes.length);
buf.putInt(axisBytes.length);
buf.putInt(encodedTypeBytes.length);
buf.put(typeNameBytes);
buf.put(namespaceBytes);
buf.put(attrBytes);
buf.put(axisBytes);
buf.put(encodedTypeBytes);
buf.put(secondaryIndexBytes);
return buf.array();
}
/**
* Extract the feature type default data from the binary stream passed in
* and place in the reprojected SFT for this feature data adapter
*
* @return if successful, the reprojected feature type created from the
* serialized stream
*/
@Override
protected Object defaultTypeDataFromBinary(
final byte[] bytes ) {
try {
FeatureDataUtils.initClassLoader();
}
catch (final MalformedURLException e) {
LOGGER.warn(
"Unable to initialize GeoTools classloader",
e);
}
// deserialize the feature type
final ByteBuffer buf = ByteBuffer.wrap(bytes);
// for now...do a gentle migration
final byte versionId = buf.get();
if (versionId != VERSION) {
LOGGER.warn("Mismatched Feature Data Adapter version");
}
final byte[] typeNameBytes = new byte[buf.getInt()];
final byte[] namespaceBytes = new byte[buf.getInt()];
final byte[] attrBytes = new byte[buf.getInt()];
final byte[] axisBytes = new byte[buf.getInt()];
final byte[] encodedTypeBytes = new byte[buf.getInt()];
buf.get(typeNameBytes);
buf.get(namespaceBytes);
buf.get(attrBytes);
buf.get(axisBytes);
buf.get(encodedTypeBytes);
final String typeName = StringUtils.stringFromBinary(typeNameBytes);
String namespace = StringUtils.stringFromBinary(namespaceBytes);
if (namespace.length() == 0) {
namespace = null;
}
// 24 bytes is the 6 four byte length fields and one byte for the
// version
final byte[] secondaryIndexBytes = new byte[bytes.length - axisBytes.length - typeNameBytes.length
- namespaceBytes.length - attrBytes.length - encodedTypeBytes.length - 29];
buf.get(secondaryIndexBytes);
final String encodedType = StringUtils.stringFromBinary(encodedTypeBytes);
try {
final SimpleFeatureType myType = FeatureDataUtils.decodeType(
namespace,
typeName,
encodedType,
StringUtils.stringFromBinary(axisBytes));
final SimpleFeatureUserDataConfigurationSet userDataConfiguration = new SimpleFeatureUserDataConfigurationSet();
userDataConfiguration.addConfigurations(
typeName,
new TimeDescriptorConfiguration(
myType));
userDataConfiguration.addConfigurations(
typeName,
new SimpleFeatureStatsConfigurationCollection(
myType));
userDataConfiguration.addConfigurations(
typeName,
new VisibilityConfiguration(
myType));
try {
userDataConfiguration.fromJsonString(
StringUtils.stringFromBinary(attrBytes),
myType);
}
catch (final IOException e) {
LOGGER.error(
"Failure to decode simple feature user data configuration",
e);
}
setFeatureType(myType);
// advertise the reprojected type externally
return reprojectedFeatureType;
}
catch (final SchemaException e) {
LOGGER.error(
"Unable to deserialized feature type",
e);
}
secondaryIndexManager = PersistenceUtils.fromBinary(
secondaryIndexBytes,
SecondaryIndexManager.class);
return null;
}
@Override
public ByteArrayId getAdapterId() {
return new ByteArrayId(
StringUtils.stringToBinary(reprojectedFeatureType.getTypeName()));
}
@Override
public boolean isSupported(
final SimpleFeature entry ) {
return reprojectedFeatureType.getName().getURI().equals(
entry.getType().getName().getURI());
}
@Override
public ByteArrayId getDataId(
final SimpleFeature entry ) {
return new ByteArrayId(
StringUtils.stringToBinary(entry.getID()));
}
private FeatureRowBuilder builder;
@Override
protected RowBuilder<SimpleFeature, Object> newBuilder() {
if (builder == null) {
builder = new FeatureRowBuilder(
reprojectedFeatureType);
}
return builder;
}
@Override
public SimpleFeatureType getFeatureType() {
return reprojectedFeatureType;
}
@Override
public AdapterPersistenceEncoding encode(
final SimpleFeature entry,
final CommonIndexModel indexModel ) {
return super.encode(
FeatureDataUtils.defaultCRSTransform(
entry,
persistedFeatureType,
reprojectedFeatureType,
transform),
indexModel);
}
@Override
public ByteArrayId[] getSupportedStatisticsTypes() {
return statsManager.getSupportedStatisticsIds();
}
@Override
public DataStatistics<SimpleFeature> createDataStatistics(
final ByteArrayId statisticsId ) {
return statsManager.createDataStatistics(
this,
statisticsId);
}
@Override
public EntryVisibilityHandler<SimpleFeature> getVisibilityHandler(
final ByteArrayId statisticsId ) {
return statsManager.getVisibilityHandler(statisticsId);
}
@Override
public boolean hasTemporalConstraints() {
return getTimeDescriptors().hasTime();
}
public synchronized void resetTimeDescriptors() {
timeDescriptors = inferTimeAttributeDescriptor(persistedFeatureType);
}
@Override
public synchronized TimeDescriptors getTimeDescriptors() {
if (timeDescriptors == null) {
timeDescriptors = inferTimeAttributeDescriptor(persistedFeatureType);
}
return timeDescriptors;
}
/**
* Determine if a time or range descriptor is set. If so, then use it,
* otherwise infer.
*
* @param persistType
* - FeatureType that will be scanned for TimeAttributes
* @return
*/
protected static final TimeDescriptors inferTimeAttributeDescriptor(
final SimpleFeatureType persistType ) {
final TimeDescriptorConfiguration config = new TimeDescriptorConfiguration(
persistType);
final TimeDescriptors timeDescriptors = new TimeDescriptors(
persistType,
config);
// Up the meta-data so that it is clear and visible any inference that
// has occurred here. Also, this is critical to
// serialization/deserialization
config.updateType(persistType);
return timeDescriptors;
}
@Override
public HadoopWritableSerializer<SimpleFeature, FeatureWritable> createWritableSerializer() {
return new FeatureWritableSerializer(
reprojectedFeatureType);
}
private static class FeatureWritableSerializer implements
HadoopWritableSerializer<SimpleFeature, FeatureWritable>
{
private final FeatureWritable writable;
FeatureWritableSerializer(
final SimpleFeatureType type ) {
writable = new FeatureWritable(
type);
}
@Override
public FeatureWritable toWritable(
final SimpleFeature entry ) {
writable.setFeature(entry);
return writable;
}
@Override
public SimpleFeature fromWritable(
final FeatureWritable writable ) {
return writable.getFeature();
}
}
@Override
public List<SecondaryIndex<SimpleFeature>> getSupportedSecondaryIndices() {
return secondaryIndexManager.getSupportedSecondaryIndices();
}
private transient final BiMap<ByteArrayId, Integer> fieldToPositionMap = HashBiMap.create();
private transient BiMap<Integer, ByteArrayId> positionToFieldMap = null;
private transient final Map<String, List<ByteArrayId>> modelToDimensionsMap = new HashMap<>();
@Override
public int getPositionOfOrderedField(
final CommonIndexModel model,
final ByteArrayId fieldId ) {
final List<ByteArrayId> dimensionFieldIds = getDimensionFieldIds(model);
// first check CommonIndexModel dimensions
if (dimensionFieldIds.contains(fieldId)) {
return dimensionFieldIds.indexOf(fieldId);
}
if (fieldToPositionMap.isEmpty()) {
initializePositionMaps();
}
// next check other fields
// dimension fields must be first, add padding
Integer position = fieldToPositionMap.get(fieldId);
if (position == null) {
return -1;
}
return position.intValue() + model.getDimensions().length;
}
@Override
public ByteArrayId getFieldIdForPosition(
final CommonIndexModel model,
final int position ) {
if (position >= model.getDimensions().length) {
if (fieldToPositionMap.isEmpty()) {
initializePositionMaps();
}
final int adjustedPosition = position - model.getDimensions().length;
// check other fields
return positionToFieldMap.get(adjustedPosition);
}
final List<ByteArrayId> dimensionFieldIds = getDimensionFieldIds(model);
// otherwise check CommonIndexModel dimensions
return dimensionFieldIds.get(position);
}
private void initializePositionMaps() {
try {
for (int i = 0; i < reprojectedFeatureType.getAttributeCount(); i++) {
final AttributeDescriptor ad = reprojectedFeatureType.getDescriptor(i);
final ByteArrayId currFieldId = new ByteArrayId(
ad.getLocalName());
fieldToPositionMap.forcePut(
currFieldId,
i);
}
positionToFieldMap = fieldToPositionMap.inverse();
}
catch (final Exception e) {
LOGGER.error(
"Unable to initialize position map, continuing anyways",
e);
}
}
private List<ByteArrayId> getDimensionFieldIds(
final CommonIndexModel model ) {
final List<ByteArrayId> retVal = modelToDimensionsMap.get(model.getId());
if (retVal != null) {
return retVal;
}
final List<ByteArrayId> dimensionFieldIds = new ArrayList<>();
for (final NumericDimensionField<? extends CommonIndexValue> dimension : model.getDimensions()) {
dimensionFieldIds.add(dimension.getFieldId());
}
modelToDimensionsMap.put(
model.getId(),
dimensionFieldIds);
return dimensionFieldIds;
}
}