Summary Table

Categories Total Count
PII 0
URL 0
DNS 0
EKL 0
IP 0
PORT 0
VsID 0
CF 0
AI 0
VPD 0
PL 0
Other 0

File Content

package gov.va.med.pbm.ampl.utility.operations;

import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Collectors;

import gov.va.med.pbm.ampl.utility.DateUtility;
import gov.va.med.pbm.ampl.utility.PropertyReflection;
import gov.va.med.pbm.ampl.utility.operations.criteria.SearchCriteria;

/**
* The FilterSearch class allows the API to filter a given collection of objects.
*
* @author Ian Meinert
*
* @param <T> The Generic type representing the class of the object which the filter will be performed.
*/
public class FilterSearch<T> extends PropertyReflection {

private SearchCriteria searchCriteria;

private Collection<T> collection;

/**
* The overloaded constructor.
*
* @param collection collection of type T
*/
public FilterSearch(Collection<T> collection) {
this.collection = collection;
}

/**
* This method applies a filter to the collection, based on the {@link SearchCriteria} provided.
* <p>
* When the {@link SearchOperators} enum of EQUALITY or CONTAINS is provided, a stream filter is applied to the collection.
* Otherwise, the collection removes the item from the stack using removeif.
*/
public void doApplyFilter() {
if (searchCriteria.getOperation().equals(SearchOperators.EQUALITY)) {
Collection<T> filteredCollection = new ArrayList<>();

collection.stream().filter(p -> {
boolean success = false;

String key = searchCriteria.getKey();
Optional<Object> optProp = Optional.ofNullable(get(p, key));

if (optProp.isPresent()) {
String propVal = optProp.get().toString();
success = this.doFilter(searchCriteria, propVal);
}

if (success) {
filteredCollection.add(p);
}

return success;
}).collect(Collectors.toList());

this.collection = filteredCollection;
} else {
// Stream the collection and apply the filter
this.collection.removeIf(p -> {
boolean doRemove = true;

String key = searchCriteria.getKey();
Optional<Object> optProp = Optional.ofNullable(get(p, key));

if (optProp.isPresent()) {
String propVal = optProp.get().toString();
doRemove = !this.doFilter(searchCriteria, propVal);
}

return doRemove;
});
}
}

/**
* This method applies the filter to the property using the criteria.
*
* @param criteria the {@link SearchCriteria}
* @param propValue the property value
* @return boolean
*/
private boolean doFilter(SearchCriteria criteria, String propValue) {
boolean success = false;
// The search operator currently being used for comparison
SearchOperators operators = criteria.getOperation();

// Get the value of the property. The get extension returns value:property. So split and get first in stack
String propertyValue = propValue.toLowerCase(Locale.ENGLISH);

// Get the value of the criteria
String criteriaValue = criteria.getValue().toString().toLowerCase(Locale.ENGLISH);

// Get a comparison result of the two properties
int compareResult = this.doCompareValues(propertyValue, criteriaValue, operators);

switch (operators) {
case GREATER_THAN:
success = compareResult > 0;
break;
case GREATER_THAN_OR_EQUAL_TO:
success = compareResult >= 0;
break;
case LESS_THAN:
success = compareResult < 0;
break;
case LESS_THAN_OR_EQUAL_TO:
success = compareResult <= 0;
break;
case CONTAINS:
success = propertyValue.contains(criteriaValue);
break;
case NEGATION:
success = compareResult != 0;
break;
case EQUALITY:
success = compareResult == 0;
break;
default:
break;
}

return success;
}

/**
* This method performs a comparison of two Strings.
* <p>
* It will perform a date validation on both Strings then compare using different date comparators, contains(), or
* compareTo() methods.
*
* @param propertyValue the propertyValue
* @param criteriaValue the criteriaValue
* @param operators the {@link SearchOperators}
* @return int
*/
private int doCompareValues(String propertyValue, String criteriaValue, SearchOperators operators) {
int result = 0;

boolean isDateComparable = DateUtility.isValidDateFormat(propertyValue) && DateUtility.isValidDateFormat(criteriaValue);

if (isDateComparable) {
OffsetDateTime date1 = DateUtility.offsetDateTimeValueOf(propertyValue);
OffsetDateTime date2 = DateUtility.offsetDateTimeValueOf(criteriaValue);

if (date1.isAfter(date2)) {
return 1;
} else if (date1.isBefore(date2)) {
return -1;
} else if (date1.equals(date2)) {
return 0;
}
} else {
String lPropVal = propertyValue.toLowerCase(Locale.ENGLISH);
String lCritVal = criteriaValue.toLowerCase(Locale.ENGLISH);

if (operators.equals(SearchOperators.CONTAINS)) {
result = lPropVal.contains(lCritVal) ? 1 : -1;
} else {
result = lPropVal.compareTo(lCritVal);
}
}

return result;
}

/**
* The getter for the required {@link SearchCriteria} when applying a filter.
*
* @return the searchCriteria
*/
public SearchCriteria getSearchCriteria() {
return searchCriteria;
}

/**
* The setter for the required {@link SearchCriteria} when applying a filter.
*
* @param searchCriteria the {@link SearchCriteria} to set
*/
public void setSearchCriteria(SearchCriteria searchCriteria) {
this.searchCriteria = searchCriteria;
}

/**
* The getter for the required collection of type T being filtered.
*
* @return the collection
*/
public Collection<T> getCollection() {
return collection;
}
}