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 OR operation.
*
* @author Will Baumann
* @since July 08, 2014
*
*/
public abstract class OrFilter<T> implements Filter<T> {
private static final String OR_FILTERS = "or_filters";
private final CopyOnWriteArrayList<Filter<T>> mFilters;
/**
* Additional logical OR calls may be added to this composite {@link Filter} via
* the {@link #or(Filter)} method.
*/
public OrFilter() {
mFilters = new CopyOnWriteArrayList<>();
}
/**
* A preset list of logical OR filters may be added to this constructor so that
* chaining via the {@link #or(Filter)} method is not required
*
* @param filters - the {@link List} of {@link Filter} to add
*/
public OrFilter(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 OrFilter(JSONObject json) throws JSONException {
final List<Filter<T>> filters = new ArrayList<>();
final JSONArray filtersArray = json.getJSONArray(OR_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 OR 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 OrFilter} for method chaining
*/
public OrFilter<T> or(final Filter<T> filter) {
mFilters.add(filter);
return this;
}
@Override
public boolean accept(final T t) {
for (final Filter<T> filter : mFilters) {
if (filter.accept(t)) {
return true;
}
}
return false;
}
@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(OR_FILTERS, filtersArray);
return json;
}
@Override
public List<Filter<T>> getChildren() {
return new ArrayList<>(mFilters);
}
@Override
public int getNameResource() {
return R.string.filter_name_or;
}
@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 || getClass() != obj.getClass()) {
return false;
}
OrFilter<?> other = (OrFilter<?>) obj;
if (mFilters == null) {
if (other.mFilters != null) {
return false;
}
}
else if (!mFilters.equals(other.mFilters)) {
return false;
}
return true;
}
}