package co.smartreceipts.android.filters;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import co.smartreceipts.android.R;
/**
* A filter implementation that combines multiple other {@link Filter}
* implementations in the manner of a logical AND operation.
*
* @author Will Baumann
* @since July 12, 2014
*
*/
public abstract class AndFilter<T> implements Filter<T> {
private static final String AND_FILTERS = "and_filters";
private final CopyOnWriteArrayList<Filter<T>> mFilters;
/**
* Additional logical AND calls may be added to this composite
* {@link Filter} via the {@link #and(Filter)} method.
*/
public AndFilter() {
mFilters = new CopyOnWriteArrayList<>();
}
/**
* A preset list of logical AND filters may be added to this constructor so
* that chaining via the {@link #and(Filter)} method is not required
*
* @param filters
* - the {@link List} of {@link Filter} to add
*/
public AndFilter(List<Filter<T>> filters) {
mFilters = new CopyOnWriteArrayList<>(filters);
}
/**
* A package-private constructor that enables us to recreate this filter via
* a {@link JSONObject} representation
*
* @param json
* - the {@link JSONObject} representation of this filter
* @throws JSONException
* - throw if our provide {@link JSONObject} is invalid
*/
protected AndFilter(JSONObject json) throws JSONException {
final List<Filter<T>> filters = new ArrayList<>();
final JSONArray filtersArray = json.getJSONArray(AND_FILTERS);
for (int i = 0; i < filtersArray.length(); i++) {
filters.add(getFilter(filtersArray.getJSONObject(i)));
}
mFilters = new CopyOnWriteArrayList<>(filters);
}
/**
* Retrieves a {@link Filter} implementation from a given JSON object. This
* is required in order to properly reconstruct our filters from JSON
*
* @param json
* - the {@link JSONObject} representing a particular filter
* @return a {@link Filter} implementation
* @throws JSONException
* - throw if our provide {@link JSONObject} is invalid
*/
abstract Filter<T> getFilter(JSONObject json) throws JSONException;
/**
* Adds another filter for the logical AND comparison that will be performed
* via the {@link #accept(Object)} method is called.
*
* @param filter
* - the {@link Filter} to add
* @return this instance of {@link AndFilter} for method chaining
*/
public AndFilter<T> and(final Filter<T> filter) {
mFilters.add(filter);
return this;
}
@Override
public boolean accept(T t) {
// iterating through all filters
// and return false if one of the filter is not accepted
for (final Filter<T> filter : mFilters) {
if (!filter.accept(t)) {
return false;
}
}
return true;
}
@Override
public JSONObject getJsonRepresentation() throws JSONException {
final JSONArray filtersArray = new JSONArray();
for (final Filter<T> filter : mFilters) {
filtersArray.put(filter.getJsonRepresentation());
}
final JSONObject json = new JSONObject();
json.put(FilterFactory.CLASS_NAME, this.getClass().getName());
json.put(AND_FILTERS, filtersArray);
return json;
}
@Override
public List<Filter<T>> getChildren() {
return new ArrayList<>(mFilters);
}
@Override
public int getNameResource() {
return R.string.filter_name_and;
}
@Override
public FilterType getType() {
return FilterType.Composite;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((mFilters == null) ? 0 : mFilters.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AndFilter<?> other = (AndFilter<?>) obj;
if (mFilters == null) {
if (other.mFilters != null)
return false;
} else if (!mFilters.equals(other.mFilters))
return false;
return true;
}
}