package mil.nga.giat.geowave.adapter.vector.query.cql; import java.net.MalformedURLException; import java.nio.ByteBuffer; import mil.nga.giat.geowave.adapter.vector.GeotoolsFeatureDataAdapter; import mil.nga.giat.geowave.adapter.vector.util.FeatureDataUtils; import mil.nga.giat.geowave.core.index.PersistenceUtils; import mil.nga.giat.geowave.core.index.StringUtils; import mil.nga.giat.geowave.core.store.adapter.AbstractAdapterPersistenceEncoding; import mil.nga.giat.geowave.core.store.adapter.IndexedAdapterPersistenceEncoding; import mil.nga.giat.geowave.core.store.data.IndexedPersistenceEncoding; 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.filter.DistributableQueryFilter; import mil.nga.giat.geowave.core.store.index.CommonIndexModel; import mil.nga.giat.geowave.core.store.index.PrimaryIndex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.geotools.filter.text.cql2.CQLException; import org.geotools.filter.text.ecql.ECQL; import org.opengis.feature.simple.SimpleFeature; import org.opengis.filter.Filter; public class CQLQueryFilter implements DistributableQueryFilter { private final static Logger LOGGER = LoggerFactory.getLogger(CQLQueryFilter.class); private GeotoolsFeatureDataAdapter adapter; private Filter filter; protected CQLQueryFilter() { super(); } public CQLQueryFilter( final Filter filter, final GeotoolsFeatureDataAdapter adapter ) { try { // We do not have a way to transform a filter directly from one to // another. this.filter = FilterToCQLTool.toFilter(FilterToCQLTool.toCQL(filter)); } catch (CQLException e) { LOGGER.trace( "Filter is not a CQL Expression", e); this.filter = filter; } this.adapter = adapter; } @Override public boolean accept( final CommonIndexModel indexModel, final IndexedPersistenceEncoding persistenceEncoding ) { if ((filter != null) && (indexModel != null) && (adapter != null)) { if (adapter.getAdapterId().equals( persistenceEncoding.getAdapterId())) { final PersistentDataset<Object> adapterExtendedValues = new PersistentDataset<Object>(); if (persistenceEncoding instanceof AbstractAdapterPersistenceEncoding) { ((AbstractAdapterPersistenceEncoding) persistenceEncoding).convertUnknownValues( adapter, indexModel); final PersistentDataset<Object> existingExtValues = ((AbstractAdapterPersistenceEncoding) persistenceEncoding) .getAdapterExtendedData(); if (existingExtValues != null) { for (final PersistentValue<Object> val : existingExtValues.getValues()) { adapterExtendedValues.addValue(val); } } } final IndexedAdapterPersistenceEncoding encoding = new IndexedAdapterPersistenceEncoding( persistenceEncoding.getAdapterId(), persistenceEncoding.getDataId(), persistenceEncoding.getIndexInsertionId(), persistenceEncoding.getDuplicateCount(), persistenceEncoding.getCommonData(), new PersistentDataset<byte[]>(), adapterExtendedValues); final SimpleFeature feature = adapter.decode( encoding, new PrimaryIndex( null, // because we know the feature data // adapter doesn't use the numeric index // strategy and only the common index // model to decode the simple feature, // we pass along a null strategy to // eliminate the necessity to send a // serialization of the strategy in the // options of this iterator indexModel)); if (feature == null) { return false; } return filter.evaluate(feature); } } return true; } @Override public byte[] toBinary() { byte[] filterBytes; if (filter == null) { LOGGER.warn("CQL filter is null"); filterBytes = new byte[] {}; } else { filterBytes = StringUtils.stringToBinary(FilterToCQLTool.toCQL(filter)); } byte[] adapterBytes; if (adapter != null) { adapterBytes = PersistenceUtils.toBinary(adapter); } else { LOGGER.warn("Feature Data Adapter is null"); adapterBytes = new byte[] {}; } final ByteBuffer buf = ByteBuffer.allocate(filterBytes.length + adapterBytes.length + 4); buf.putInt(filterBytes.length); buf.put(filterBytes); buf.put(adapterBytes); return buf.array(); } @Override public void fromBinary( final byte[] bytes ) { try { FeatureDataUtils.initClassLoader(); } catch (final MalformedURLException e) { LOGGER.error( "Unable to initialize GeoTools class loader", e); } final ByteBuffer buf = ByteBuffer.wrap(bytes); final int filterBytesLength = buf.getInt(); final int adapterBytesLength = bytes.length - filterBytesLength - 4; if (filterBytesLength > 0) { final byte[] filterBytes = new byte[filterBytesLength]; buf.get(filterBytes); final String cql = StringUtils.stringFromBinary(filterBytes); try { filter = ECQL.toFilter(cql); } catch (final Exception e) { throw new IllegalArgumentException( cql, e); } } else { LOGGER.warn("CQL filter is empty bytes"); filter = null; } if (adapterBytesLength > 0) { final byte[] adapterBytes = new byte[adapterBytesLength]; buf.get(adapterBytes); try { adapter = PersistenceUtils.fromBinary( adapterBytes, GeotoolsFeatureDataAdapter.class); } catch (final Exception e) { throw new IllegalArgumentException( e); } } else { LOGGER.warn("Feature Data Adapter is empty bytes"); adapter = null; } } }