package mil.nga.giat.geowave.adapter.vector.stats; 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.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.referencing.operation.MathTransform; import com.vividsolutions.jts.geom.Geometry; import mil.nga.giat.geowave.core.geotime.TimeUtils; import mil.nga.giat.geowave.core.geotime.store.dimension.GeometryWrapper; import mil.nga.giat.geowave.core.index.ByteArrayId; import mil.nga.giat.geowave.core.store.EntryVisibilityHandler; import mil.nga.giat.geowave.core.store.adapter.DataAdapter; import mil.nga.giat.geowave.core.store.adapter.statistics.AbstractDataStatistics; import mil.nga.giat.geowave.core.store.adapter.statistics.CountDataStatistics; import mil.nga.giat.geowave.core.store.adapter.statistics.DataStatistics; import mil.nga.giat.geowave.core.store.adapter.statistics.FieldIdStatisticVisibility; import mil.nga.giat.geowave.core.store.adapter.statistics.FieldTypeStatisticVisibility; /** * Object that manages statistics for an adapter */ public class StatsManager { private final static Logger LOGGER = LoggerFactory.getLogger(StatsManager.class); /** * Visibility that can be used within GeoWave as a CommonIndexValue */ private final static EntryVisibilityHandler<SimpleFeature> GEOMETRY_VISIBILITY_HANDLER = new FieldTypeStatisticVisibility<SimpleFeature>( GeometryWrapper.class); /** * List of stats objects supported by this manager for the adapter */ private final List<DataStatistics<SimpleFeature>> statsObjList = new ArrayList<DataStatistics<SimpleFeature>>(); /** * List of visibility handlers supported by this manager for the stats * objects */ private final Map<ByteArrayId, EntryVisibilityHandler<SimpleFeature>> visibilityHandlers = new HashMap<ByteArrayId, EntryVisibilityHandler<SimpleFeature>>(); // ----------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------- /** * Constructor - Creates a StatsManager that supports the specified adapter * with a persisted SimpleFeatureType * * @param dataAdapter * - adapter to be associated with this manager * @param persistedType * - the feature type to be associated with the given adapter */ public StatsManager( final DataAdapter<SimpleFeature> dataAdapter, final SimpleFeatureType persistedType ) { this( dataAdapter, persistedType, null, null); } // ----------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------- /** * Constructor - Creates a StatsManager that supports the specified adapter * with a persisted and re-projected SimpleFeatureType that is translated by * the transform * * @param dataAdapter * - adapter to be associated with this manager * @param persistedType * - the feature type to be associated with the given adapter * @param reprojectedType * - feature type re-projected with new CRS. Used if stats object * is a Feature Bounding Box. * @param transform * - type of transform applied to persisted type. Used if stats * object is a Feature Bounding Box. */ public StatsManager( final DataAdapter<SimpleFeature> dataAdapter, final SimpleFeatureType persistedType, final SimpleFeatureType reprojectedType, final MathTransform transform ) { // For all the attribute descriptors listed in the persisted // featuretype, // go through and look for stats that can be tracked against this // attribute type for (final AttributeDescriptor descriptor : persistedType.getAttributeDescriptors()) { String fieldName = descriptor.getLocalName(); FieldIdStatisticVisibility vis = new FieldIdStatisticVisibility( new ByteArrayId( fieldName)); ByteArrayId adapterID = dataAdapter.getAdapterId(); // --------------------------------------------------------------------- // For temporal and geometry because there is a dependency on these // stats for optimizations within the GeoServer adapter. if (TimeUtils.isTemporal(descriptor.getType().getBinding())) { FeatureTimeRangeStatistics statObj = new FeatureTimeRangeStatistics( adapterID, fieldName); addStats( statObj, vis); } else if (Geometry.class.isAssignableFrom(descriptor.getType().getBinding())) { FeatureBoundingBoxStatistics statObj = new FeatureBoundingBoxStatistics( adapterID, fieldName, persistedType, reprojectedType, transform); addStats( statObj, vis); } // --------------------------------------------------------------------- // If there is a stats field in the feature, then that is a // StatsConfigurationCollection and the stats objects for the // feature need to be extracted and added as stats for this adapter if (descriptor.getUserData().containsKey( "stats")) { final StatsConfigurationCollection statsConfigurations = (StatsConfigurationCollection) descriptor .getUserData() .get( "stats"); List<StatsConfig<SimpleFeature>> featureConfigs = statsConfigurations.getConfigurationsForAttribute(); for (StatsConfig<SimpleFeature> statConfig : featureConfigs) { addStats( statConfig.create( adapterID, fieldName), vis); } } // --------------------------------------------------------------------- // If this numeric, then add two stats objects that relate else if (Number.class.isAssignableFrom(descriptor.getType().getBinding())) { addStats( new FeatureNumericRangeStatistics( adapterID, fieldName), vis); addStats( new FeatureFixedBinNumericStatistics( adapterID, fieldName), vis); } } } // ----------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------- /** * Creates a stats object to be tracked for this adapter based on type * * @param dataAdapter * - adapter to be associated with this manager * @param statisticsId * - name of statistics type to be created * * @return new statistics object of specified type */ public DataStatistics<SimpleFeature> createDataStatistics( final DataAdapter<SimpleFeature> dataAdapter, final ByteArrayId statisticsId ) { for (final DataStatistics<SimpleFeature> statObj : statsObjList) { if (statObj.getStatisticsId().equals( statisticsId)) { // TODO most of the data statistics seem to do shallow clones // that pass along a lot of references - this seems // counter-intuitive to the spirit of a "create" method, but it // seems to work right now? return ((AbstractDataStatistics<SimpleFeature>) statObj).duplicate(); } // LOGGER.warn("Comparing ID '" + statisticsId.getString() + "' and // '" + stat.getStatisticsId().getString() + "'"); } if (statisticsId.getString().equals( CountDataStatistics.STATS_TYPE.getString())) { return new CountDataStatistics<SimpleFeature>( dataAdapter.getAdapterId()); } // HP Fortify "Log Forging" false positive // What Fortify considers "user input" comes only // from users with OS-level access anyway LOGGER.warn("Unrecognized statistics ID " + statisticsId.getString() + ", using count statistic."); return new CountDataStatistics<SimpleFeature>( dataAdapter.getAdapterId(), statisticsId); } // ----------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------- /** * Get a visibility handler for the specified statistics object * * @param statisticsId * @return - visibility handler for given stats object */ public EntryVisibilityHandler<SimpleFeature> getVisibilityHandler( final ByteArrayId statisticsId ) { // If the statistics object is of type CountDataStats or there is no // visibility handler, then return the default visibility handler if (statisticsId.equals(CountDataStatistics.STATS_TYPE) || (!visibilityHandlers.containsKey(statisticsId))) { return GEOMETRY_VISIBILITY_HANDLER; } return visibilityHandlers.get(statisticsId); } // ----------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------- /** * Adds/Replaces a stats object for the given adapter <br> * Supports object replacement. * * @param statsObj * - data stats object to be tracked by adding or replacement * @param visibilityHandler * - type of visibility required to access the stats object * */ public void addStats( DataStatistics<SimpleFeature> statsObj, EntryVisibilityHandler<SimpleFeature> visibilityHandler ) { int replaceStat = 0; // Go through stats list managed by this manager and look for a match for (DataStatistics<SimpleFeature> currentStat : statsObjList) { if (currentStat.getStatisticsId().equals( statsObj.getStatisticsId())) { // If a match was found for an existing stat object in list, // remove it now and replace it later. this.statsObjList.remove(replaceStat); break; } replaceStat++; // Not found, check next stat object } this.statsObjList.add(statsObj); this.visibilityHandlers.put( statsObj.getStatisticsId(), visibilityHandler); } // ----------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------- /** * Get an array of stats object IDs for the Stats Manager * * @return Array of stats object IDs as 'ByteArrayId' */ public ByteArrayId[] getSupportedStatisticsIds() { // Why are we adding a CountDataStatistics?? final ByteArrayId[] statObjIds = new ByteArrayId[statsObjList.size() + 1]; int i = 0; for (final DataStatistics<SimpleFeature> statObj : statsObjList) { statObjIds[i++] = statObj.getStatisticsId(); } statObjIds[i] = CountDataStatistics.STATS_TYPE; return statObjIds; } }