/**
* Copyright (c) Codice Foundation
* <p>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*/
package ddf.catalog.impl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import org.geotools.filter.visitor.DefaultFilterVisitor;
import org.opengis.filter.Not;
import org.opengis.filter.PropertyIsNil;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.temporal.After;
import org.opengis.filter.temporal.Before;
import org.opengis.filter.temporal.Begins;
import org.opengis.filter.temporal.BegunBy;
import org.opengis.filter.temporal.During;
import org.opengis.filter.temporal.EndedBy;
import org.opengis.filter.temporal.Ends;
import org.opengis.filter.temporal.Meets;
import org.opengis.filter.temporal.MetBy;
import org.opengis.filter.temporal.OverlappedBy;
import org.opengis.filter.temporal.TContains;
import org.opengis.filter.temporal.TEquals;
import org.opengis.filter.temporal.TOverlaps;
import org.opengis.temporal.Instant;
import org.opengis.temporal.Period;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ddf.catalog.data.ContentType;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.Result;
import ddf.catalog.data.impl.MetacardImpl;
import ddf.catalog.data.impl.ResultImpl;
import ddf.catalog.operation.CreateRequest;
import ddf.catalog.operation.CreateResponse;
import ddf.catalog.operation.DeleteRequest;
import ddf.catalog.operation.DeleteResponse;
import ddf.catalog.operation.QueryRequest;
import ddf.catalog.operation.SourceResponse;
import ddf.catalog.operation.Update;
import ddf.catalog.operation.UpdateRequest;
import ddf.catalog.operation.UpdateResponse;
import ddf.catalog.operation.impl.CreateResponseImpl;
import ddf.catalog.operation.impl.DeleteResponseImpl;
import ddf.catalog.operation.impl.SourceResponseImpl;
import ddf.catalog.operation.impl.UpdateImpl;
import ddf.catalog.operation.impl.UpdateResponseImpl;
import ddf.catalog.source.CatalogProvider;
public class MockMemoryProvider extends MockSource implements CatalogProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(MockMemoryProvider.class);
private HashMap<Serializable, Metacard> store;
private boolean hasReceivedRead = false;
private boolean hasReceivedCreate = false;
private boolean hasReceivedUpdate = false;
private boolean hasReceivedDelete = false;
private boolean hasReceivedUpdateByIdentifier = false;
private boolean hasReceivedDeleteByIdentifier = false;
private boolean hasReceivedQuery = false;
private String sourceId;
public MockMemoryProvider(String id, boolean isAvailable) {
this(id, "", "", "", new HashSet<>(), isAvailable, new Date());
sourceId = id;
}
/**
* Mock provider, saves entries in memory. Cannot perform queries.
*
* @param shortName
* @param title
* @param version
* @param organization
* @param catalogTypes
* @param isAvailable
* @param lastAvailability
*/
public MockMemoryProvider(String shortName, String title, String version, String organization,
Set<ContentType> catalogTypes, boolean isAvailable, Date lastAvailability) {
super(shortName, title, version, organization, catalogTypes, isAvailable, lastAvailability);
store = new HashMap<>();
sourceId = "mockMemoryProvider";
}
@Override
public CreateResponse create(CreateRequest request) {
List<Metacard> oldCards = request.getMetacards();
hasReceivedCreate = true;
List<Metacard> returnedMetacards = new ArrayList<>(oldCards.size());
Map<String, Serializable> properties = new HashMap<>();
for (Metacard curCard : oldCards) {
MetacardImpl card = new MetacardImpl(curCard);
if (card.getId() == null) {
card.setId(UUID.randomUUID()
.toString());
}
LOGGER.debug("Storing metacard with id: {}", card.getId());
store.put(card.getId(), card);
properties.put(card.getId(), card);
returnedMetacards.add(card);
}
CreateResponse ingestResponseImpl = new CreateResponseImpl(request,
properties,
returnedMetacards);
return ingestResponseImpl;
}
@Override
public UpdateResponse update(UpdateRequest request) {
String methodName = "update";
LOGGER.debug("Entering: {}", methodName);
hasReceivedUpdate = true;
hasReceivedUpdateByIdentifier = true;
List<Entry<Serializable, Metacard>> updatedCards = request.getUpdates();
Map<String, Serializable> properties = new HashMap<>();
List<Update> returnedMetacards = new ArrayList<>(updatedCards.size());
for (Entry<Serializable, Metacard> curCard : updatedCards) {
if (store.containsKey(curCard.getValue()
.getId())) {
LOGGER.debug("Store contains the key");
Metacard oldMetacard = store.get(curCard.getValue()
.getId());
store.put(curCard.getValue()
.getId(), curCard.getValue());
properties.put(curCard.getValue()
.getId(), curCard.getValue());
LOGGER.debug("adding returnedMetacard");
returnedMetacards.add(new UpdateImpl(curCard.getValue(), oldMetacard));
} else {
LOGGER.debug("Key not contained in the store");
}
}
UpdateResponse response = new UpdateResponseImpl(request, properties, returnedMetacards);
LOGGER.debug("Exiting:{}", methodName);
return response;
}
@Override
public DeleteResponse delete(DeleteRequest deleteRequest) {
hasReceivedDelete = true;
@SuppressWarnings("unchecked")
List<String> ids = (List<String>) deleteRequest.getAttributeValues();
Map<String, Serializable> properties = new HashMap<>();
List<Metacard> returnedMetacards = new ArrayList<>(ids.size());
for (int i = 0; i < ids.size(); i++) {
String id = (String) ids.get(i);
UUID curUUID = UUID.fromString(id);
if (store.containsKey(curUUID.toString())) {
Metacard card = store.remove(curUUID.toString());
if (card != null) {
returnedMetacards.add(card);
}
}
}
DeleteResponse response = new DeleteResponseImpl(deleteRequest,
properties,
returnedMetacards);
return response;
}
@Override
public SourceResponse query(QueryRequest query) {
// TODO currently returning all metacards, not using queryrequest
List<Result> results = new ArrayList<Result>();
MockMemoryFilterVisitor mockMemoryFilterVisitor = new MockMemoryFilterVisitor();
Collection<Metacard> filteredMetacards = (Collection<Metacard>) query.getQuery()
.accept(mockMemoryFilterVisitor, store.values());
for (Metacard metacard : filteredMetacards) {
results.add(new ResultImpl(metacard));
}
return new SourceResponseImpl(query, results);
}
public int size() {
return store.size();
}
public boolean hasReceivedQuery() {
return hasReceivedQuery;
}
public boolean hasReceivedRead() {
return hasReceivedRead;
}
public boolean hasReceivedCreate() {
return hasReceivedCreate;
}
public boolean hasReceivedUpdate() {
return hasReceivedUpdate;
}
public boolean hasReceivedDelete() {
return hasReceivedDelete;
}
public boolean hasReceivedUpdateByIdentifier() {
return hasReceivedUpdateByIdentifier;
}
public boolean hasReceivedDeleteByIdentifier() {
return hasReceivedDeleteByIdentifier;
}
@Override
public String getId() {
return sourceId;
}
@Override
public void maskId(String sourceId) {
this.sourceId = sourceId;
}
@Override
public Set<ContentType> getContentTypes() {
return new HashSet<>();
}
class MockMemoryFilterVisitor extends DefaultFilterVisitor {
@Override
public Object visit(Not filter, Object data) {
LOGGER.trace("entry {},{}", filter, data);
Set<Metacard> notFilteredSet = new HashSet<>();
notFilteredSet.addAll((Collection<? extends Metacard>) data);
Collection<Metacard> filteredSet = (Collection<Metacard>) filter.getFilter()
.accept(this, data);
notFilteredSet.removeAll(filteredSet);
LOGGER.trace("exit {}", notFilteredSet.size());
return notFilteredSet;
}
@Override
public Object visit(After after, Object data) {
LOGGER.trace("entry {},{}", after, data);
Set<Metacard> filteredSet = new HashSet<>();
PropertyName propName = (PropertyName) after.getExpression1();
Object obj = ((Literal) after.getExpression2()).getValue();
Date afterFilter;
LOGGER.debug("what is object? {}", obj);
if (obj instanceof Period) {
afterFilter = ((Period) obj).getEnding()
.getPosition()
.getDate();
} else {
afterFilter = ((Instant) obj).getPosition()
.getDate();
}
if (data instanceof Collection<?>) {
Collection<Metacard> mcData = (Collection<Metacard>) data;
for (Metacard mc : mcData) {
Date mcDate = getMetacardDate(mc, propName.getPropertyName());
if (mcDate != null) {
if (mcDate.after(afterFilter)) {
filteredSet.add(mc);
}
}
}
}
LOGGER.trace("exit {}", filteredSet);
return filteredSet;
}
@Override
public Object visit(Before before, Object data) {
LOGGER.trace("entry {},{}", before, data);
Set<Metacard> filteredSet = new HashSet<>();
PropertyName propName = (PropertyName) before.getExpression1();
Object obj = ((Literal) before.getExpression2()).getValue();
Date beforeFilter;
if (obj instanceof Period) {
beforeFilter = ((Period) obj).getBeginning()
.getPosition()
.getDate();
} else {
beforeFilter = ((Instant) obj).getPosition()
.getDate();
}
if (data instanceof Collection<?>) {
Collection<Metacard> mcData = (Collection<Metacard>) data;
for (Metacard mc : mcData) {
Date mcDate = getMetacardDate(mc, propName.getPropertyName());
if (mcDate != null) {
if (mcDate.before(beforeFilter)) {
filteredSet.add(mc);
}
}
}
}
LOGGER.trace("exit {}", filteredSet);
return filteredSet;
}
@Override
public Object visit(Begins begins, Object data) {
LOGGER.trace("entry {},{}", begins, data);
Set<Metacard> filteredSet = new HashSet<>();
PropertyName propName = (PropertyName) begins.getExpression1();
Object obj = ((Literal) begins.getExpression2()).getValue();
Date beginsFilter = ((Period) obj).getBeginning()
.getPosition()
.getDate();
if (data instanceof Collection<?>) {
Collection<Metacard> mcData = (Collection<Metacard>) data;
for (Metacard mc : mcData) {
Date mcDate = getMetacardDate(mc, propName.getPropertyName());
if (mcDate != null) {
if (mcDate.equals(beginsFilter)) {
filteredSet.add(mc);
}
}
}
}
LOGGER.trace("exit {}", filteredSet);
return filteredSet;
}
@Override
public Object visit(BegunBy begunBy, Object data) {
// API dictates that BegunBy filters only on period data fields.
// Metacard currently has no period fields.
return super.visit(begunBy, data);
}
@Override
public Object visit(During during, Object data) {
LOGGER.trace("entry {},{}", during, data);
Set<Metacard> filteredSet = new HashSet<>();
PropertyName propName = (PropertyName) during.getExpression1();
Period filterPeriod = (Period) ((Literal) during.getExpression2()).getValue();
Date startFilter = filterPeriod.getBeginning()
.getPosition()
.getDate();
Date endFilter = filterPeriod.getEnding()
.getPosition()
.getDate();
if (data instanceof Collection<?>) {
Collection<Metacard> mcData = (Collection<Metacard>) data;
for (Metacard mc : mcData) {
Date mcDate = getMetacardDate(mc, propName.getPropertyName());
if (mcDate != null) {
if (mcDate.after(startFilter) && mcDate.before(endFilter)) {
filteredSet.add(mc);
}
}
}
}
LOGGER.trace("exit {}", filteredSet);
return filteredSet;
}
@Override
public Object visit(EndedBy endedBy, Object data) {
// API dictates that EndedBy filters only on period data fields.
// Metacard currently has no period fields.
return super.visit(endedBy, data);
}
@Override
public Object visit(Ends ends, Object data) {
LOGGER.trace("entry {},{}", ends, data);
Set<Metacard> filteredSet = new HashSet<>();
PropertyName propName = (PropertyName) ends.getExpression1();
Object obj = ((Literal) ends.getExpression2()).getValue();
Date endsFilter = ((Period) obj).getEnding()
.getPosition()
.getDate();
if (data instanceof Collection<?>) {
Collection<Metacard> mcData = (Collection<Metacard>) data;
for (Metacard mc : mcData) {
Date mcDate = getMetacardDate(mc, propName.getPropertyName());
if (mcDate != null) {
if (mcDate.equals(endsFilter)) {
filteredSet.add(mc);
}
}
}
}
LOGGER.trace("exit {}", filteredSet);
return filteredSet;
}
@Override
public Object visit(Meets meets, Object data) {
// API dictates that Meets filters only on period data fields.
// Metacard currently has no period fields.
return super.visit(meets, data);
}
@Override
public Object visit(MetBy metBy, Object data) {
// API dictates that MetBy filters only on period data fields.
// Metacard currently has no period fields.
return super.visit(metBy, data);
}
@Override
public Object visit(OverlappedBy overlappedBy, Object data) {
// API dictates that OverlappedBy filters only on period data
// fields.
// Metacard currently has no period fields.
return super.visit(overlappedBy, data);
}
@Override
public Object visit(TContains contains, Object data) {
// API dictates that TContains filters only on period data fields.
// Metacard currently has no period fields.
return super.visit(contains, data);
}
@Override
public Object visit(TEquals equals, Object data) {
LOGGER.trace("entry {},{}", equals, data);
Set<Metacard> filteredSet = new HashSet<>();
PropertyName propName = (PropertyName) equals.getExpression1();
Object obj = ((Literal) equals.getExpression2()).getValue();
Date equalsFilter = ((Instant) obj).getPosition()
.getDate();
if (data instanceof Collection<?>) {
Collection<Metacard> mcData = (Collection<Metacard>) data;
for (Metacard mc : mcData) {
Date mcDate = getMetacardDate(mc, propName.getPropertyName());
if (mcDate != null) {
if (mcDate.equals(equalsFilter)) {
filteredSet.add(mc);
}
}
}
}
LOGGER.trace("exit {}", filteredSet);
return filteredSet;
}
@Override
public Object visit(TOverlaps contains, Object data) {
// API dictates that TOverlaps filters only on period data fields.
// Metacard currently has no period fields.
return super.visit(contains, data);
}
private Date getMetacardDate(Metacard mc, String propName) {
LOGGER.trace("entry");
Date d = null;
if (propName != null && !propName.isEmpty()) {
if (propName.equals(Metacard.CREATED)) {
d = mc.getCreatedDate();
} else if (propName.equals(Metacard.EFFECTIVE)) {
d = mc.getEffectiveDate();
} else if (propName.equals(Metacard.EXPIRATION)) {
d = mc.getExpirationDate();
} else if (propName.equals(Metacard.MODIFIED)) {
d = mc.getModifiedDate();
}
}
LOGGER.trace("exit {}", d);
return d;
}
@Override
public Object visit(PropertyIsNil arg0, Object arg1) {
return null;
}
}
}