package mil.nga.giat.geowave.adapter.vector; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.geotools.data.DataUtilities; import org.geotools.feature.SchemaException; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import mil.nga.giat.geowave.adapter.vector.field.SimpleFeatureSerializationProvider; import mil.nga.giat.geowave.adapter.vector.index.SimpleFeaturePrimaryIndexConfiguration; 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.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.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.IndexedAdapterPersistenceEncoding; 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.PersistentDataset; import mil.nga.giat.geowave.core.store.data.PersistentValue; 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.FieldWriter; 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.PrimaryIndex; /** * Deprecated as of 0.9.1 */ @Deprecated public class WholeFeatureDataAdapter extends AbstractDataAdapter<SimpleFeature> implements GeotoolsFeatureDataAdapter, StatisticsProvider<SimpleFeature> { private final static Logger LOGGER = LoggerFactory.getLogger(WholeFeatureDataAdapter.class); protected SimpleFeatureType featureType; private ByteArrayId adapterId; private SimpleFeatureBuilder b; private StatsManager statsManager; protected WholeFeatureDataAdapter() { super(); } public WholeFeatureDataAdapter( final SimpleFeatureType featureType ) { super( new ArrayList<PersistentIndexFieldHandler<SimpleFeature, ? extends CommonIndexValue, Object>>(), new ArrayList<NativeFieldHandler<SimpleFeature, Object>>(), null, featureType); this.featureType = featureType; adapterId = new ByteArrayId( StringUtils.stringToBinary(featureType.getTypeName())); statsManager = new StatsManager( this, featureType, featureType, null); } @Override public ByteArrayId getAdapterId() { return adapterId; } @Override public boolean isSupported( final SimpleFeature entry ) { return entry.getName().getURI().equals( featureType.getName().getURI()); } @Override public ByteArrayId getDataId( final SimpleFeature entry ) { return new ByteArrayId( StringUtils.stringToBinary(entry.getID())); } @Override public FieldReader<Object> getReader( final ByteArrayId fieldId ) { return (FieldReader) new SimpleFeatureSerializationProvider.WholeFeatureReader( featureType); } @Override public FieldWriter<SimpleFeature, Object> getWriter( final ByteArrayId fieldId ) { return (FieldWriter) new SimpleFeatureSerializationProvider.WholeFeatureWriter(); } @Override protected RowBuilder<SimpleFeature, Object> newBuilder() { return new WholeFeatureRowBuilder(); } @Override public boolean hasTemporalConstraints() { return getTimeDescriptors().hasTime(); } // FIXME copy/paste from FeatureDataAdater ... abstract! @Override protected List<IndexFieldHandler<SimpleFeature, ? extends CommonIndexValue, Object>> getDefaultTypeMatchingHandlers( final Object typeObj ) { if ((typeObj != null) && (typeObj instanceof SimpleFeatureType)) { final SimpleFeatureType internalType = (SimpleFeatureType) typeObj; nativeFieldHandlers = typeToFieldHandlers((SimpleFeatureType) typeObj); final List<IndexFieldHandler<SimpleFeature, ? extends CommonIndexValue, Object>> defaultHandlers = new ArrayList<IndexFieldHandler<SimpleFeature, ? extends CommonIndexValue, Object>>(); final IndexFieldHandler<SimpleFeature, Time, Object> timeHandler = getTimeRangeHandler(internalType); if (timeHandler != null) { defaultHandlers.add(timeHandler); } defaultHandlers.add(new FeatureGeometryHandler( internalType.getGeometryDescriptor(), fieldVisiblityHandler)); return defaultHandlers; } // LOGGER.warn("Simple Feature Type could not be used for handling the // indexed data"); return super.getDefaultTypeMatchingHandlers(featureType); } private static List<NativeFieldHandler<SimpleFeature, Object>> typeToFieldHandlers( final SimpleFeatureType type ) { final List<NativeFieldHandler<SimpleFeature, Object>> nativeHandlers = new ArrayList<NativeFieldHandler<SimpleFeature, Object>>(); nativeHandlers.add(new WholeFeatureHandler( type)); return nativeHandlers; } private IndexFieldHandler<SimpleFeature, Time, Object> getTimeRangeHandler( final SimpleFeatureType featureType ) { final TimeDescriptors timeDescriptors = inferTimeAttributeDescriptor(featureType); if ((timeDescriptors.getStartRange() != null) && (timeDescriptors.getEndRange() != null)) { return (new FeatureTimeRangeHandler( new FeatureAttributeHandler( timeDescriptors.getStartRange()), new FeatureAttributeHandler( timeDescriptors.getEndRange()), fieldVisiblityHandler)); } 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 return new FeatureTimestampHandler( timeDescriptors.getTime(), fieldVisiblityHandler); } return null; } 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 SimpleFeatureType getFeatureType() { return featureType; } @Override public synchronized TimeDescriptors getTimeDescriptors() { return inferTimeAttributeDescriptor(featureType); } @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 SimpleFeature decode( final IndexedAdapterPersistenceEncoding data, final PrimaryIndex index ) { final PersistentValue<Object> obj = data.getAdapterExtendedData().getValues().get( 0); final byte[][] bytes = (byte[][]) obj.getValue(); int i = 0; final SimpleFeatureBuilder bldr = getBuilder(); for (final byte[] f : bytes) { if (f != null) { final FieldReader reader = FieldUtils.getDefaultReaderForClass(featureType.getType( i).getBinding()); bldr.set( i, reader.readField(f)); } i++; } return bldr.buildFeature(data.getDataId().getString()); } private synchronized SimpleFeatureBuilder getBuilder() { if (b == null) { b = new SimpleFeatureBuilder( featureType); } return b; } @Override public AdapterPersistenceEncoding encode( final SimpleFeature entry, final CommonIndexModel indexModel ) { final PersistentDataset<Object> extendedData = new PersistentDataset<Object>(); extendedData.addValue(new PersistentValue<Object>( new ByteArrayId( ""), entry.getAttributes().toArray( new Object[] {}))); final AdapterPersistenceEncoding encoding = super.encode( entry, indexModel); return new WholeFeatureAdapterEncoding( getAdapterId(), getDataId(entry), encoding.getCommonData(), extendedData); } @Override protected byte[] defaultTypeDataToBinary() { final String encodedType = DataUtilities.encodeType(featureType); final String typeName = featureType.getTypeName(); final byte[] typeNameBytes = StringUtils.stringToBinary(typeName); final byte[] encodedTypeBytes = StringUtils.stringToBinary(encodedType); byte[] attrBytes = new byte[0]; final SimpleFeatureUserDataConfigurationSet userDataConfiguration = new SimpleFeatureUserDataConfigurationSet(); userDataConfiguration.addConfigurations( typeName, new TimeDescriptorConfiguration()); userDataConfiguration.addConfigurations( typeName, new SimpleFeatureStatsConfigurationCollection()); userDataConfiguration.addConfigurations( typeName, new SimpleFeaturePrimaryIndexConfiguration()); userDataConfiguration.configureFromType(featureType); try { attrBytes = StringUtils.stringToBinary(userDataConfiguration.asJsonString()); } catch (final IOException e) { LOGGER.error( "Failure to encode simple feature user data configuration", e); } final ByteBuffer buf = ByteBuffer.allocate(encodedTypeBytes.length + typeNameBytes.length + adapterId.getBytes().length + attrBytes.length + 24); buf.putInt(0); // a signal for the new version buf.putInt(typeNameBytes.length); buf.putInt(0); // old visibility (backward compatibility) buf.putInt(attrBytes.length); buf.putInt(encodedTypeBytes.length); buf.putInt(adapterId.getBytes().length); buf.put(typeNameBytes); buf.put(attrBytes); buf.put(encodedTypeBytes); buf.put(adapterId.getBytes()); return buf.array(); } @Override protected Object defaultTypeDataFromBinary( final byte[] bytes ) { final ByteBuffer buf = ByteBuffer.wrap(bytes); final int initialBytes = buf.getInt(); // temporary hack for backward compatibility final boolean skipConfig = (initialBytes > 0); final byte[] typeNameBytes = skipConfig ? new byte[initialBytes] : new byte[buf.getInt()]; final byte[] visibilityManagementClassNameBytes = new byte[buf.getInt()]; final byte[] attrBytes = skipConfig ? new byte[0] : new byte[buf.getInt()]; final byte[] encodedTypeBytes = new byte[buf.getInt()]; final byte[] adapterIdBytes = new byte[buf.getInt()]; buf.get(typeNameBytes); buf.get(visibilityManagementClassNameBytes); // ignore...old release buf.get(attrBytes); buf.get(encodedTypeBytes); buf.get(adapterIdBytes); adapterId = new ByteArrayId( adapterIdBytes); final String typeName = StringUtils.stringFromBinary(typeNameBytes); final String encodedType = StringUtils.stringFromBinary(encodedTypeBytes); try { final SimpleFeatureType myType = DataUtilities.createType( typeName, encodedType); featureType = myType; return featureType; } catch (final SchemaException e) { LOGGER.error( "Unable to deserialized feature type", e); } final SimpleFeatureUserDataConfigurationSet userDataConfiguration = new SimpleFeatureUserDataConfigurationSet(); userDataConfiguration.addConfigurations( typeName, new TimeDescriptorConfiguration( featureType)); userDataConfiguration.addConfigurations( typeName, new SimpleFeatureStatsConfigurationCollection( featureType)); userDataConfiguration.addConfigurations( typeName, new SimpleFeaturePrimaryIndexConfiguration( featureType)); try { userDataConfiguration.fromJsonString( StringUtils.stringFromBinary(attrBytes), featureType); } catch (final IOException e) { LOGGER.error( "Failure to decode simple feature user data configuration", e); } return null; } @Override public int getPositionOfOrderedField( final CommonIndexModel model, final ByteArrayId fieldId ) { return model.getDimensions().length + 1; } @Override public ByteArrayId getFieldIdForPosition( final CommonIndexModel model, final int position ) { return new ByteArrayId( ""); } }