package org.azavea.otm; import android.os.Bundle; import android.os.Handler.Callback; import android.os.Message; import com.atlassian.fugue.Either; import org.azavea.helpers.Logger; import org.azavea.otm.data.InstanceInfo; import org.azavea.otm.data.Species; import org.azavea.otm.data.SpeciesContainer; import org.azavea.otm.filters.BaseFilter; import org.azavea.otm.filters.BooleanFilter; import org.azavea.otm.filters.ChoiceFilter; import org.azavea.otm.filters.DateRangeFilter; import org.azavea.otm.filters.DefaultFilter; import org.azavea.otm.filters.MissingFilter; import org.azavea.otm.filters.MultiChoiceFilter; import org.azavea.otm.filters.NumericRangeFilter; import org.azavea.otm.filters.SpeciesFilter; import org.azavea.otm.filters.TextFilter; import org.azavea.otm.rest.RequestGenerator; import org.azavea.otm.rest.handlers.ContainerRestHandler; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class FilterManager { private RequestGenerator request = new RequestGenerator(); // All filters loaded from configuration file, set with the latest state private LinkedHashMap<String, BaseFilter> allFilters = new LinkedHashMap<>(); // List of all species received from the API private LinkedHashMap<Integer, Species> species = new LinkedHashMap<>(); private final InstanceInfo instanceInfo; public FilterManager(InstanceInfo instanceInfo) { this.instanceInfo = instanceInfo; final JSONObject filterDefinitions = instanceInfo.getSearchDefinitions(); loadSpeciesList(); loadFilterDefinitions(filterDefinitions); } public void loadSpeciesList() { loadSpeciesList(null); } public void loadSpeciesList(final Callback callback) { // If species were already lazy loaded, return immediately if (species.size() > 0 && callback != null) { handleSpeciesCallback(callback, true); return; } request.getAllSpecies(new ContainerRestHandler<SpeciesContainer>( new SpeciesContainer()) { @Override public void dataReceived(SpeciesContainer container) { try { species = (LinkedHashMap<Integer, Species>) container .getAll(); if (callback != null) { handleSpeciesCallback(callback, true); } } catch (JSONException e) { Logger.error("Error in Species retrieval", e); } } @Override public void failure(Throwable e, String message) { Logger.error(message, e); if (callback != null) { handleSpeciesCallback(callback, false); } } }); } private void handleSpeciesCallback(Callback callback, boolean success) { Message resultMessage = new Message(); Bundle data = new Bundle(); data.putBoolean("success", success); resultMessage.setData(data); callback.handleMessage(resultMessage); } private BaseFilter makeMapFilter(String key, String identifier, String label, String type, JSONArray choices, String defaultIdentifier) throws Exception { if (type.equals("BOOL")) { return new BooleanFilter(key, identifier, label); } else if (type.equals("RANGE")) { return new NumericRangeFilter(key, identifier, label); } else if (type.equals("DATERANGE")) { return new DateRangeFilter(key, identifier, label); } else if (type.equals("SPECIES")) { return new SpeciesFilter(key, identifier, label); } else if (type.equals("MISSING")) { return new MissingFilter(key, identifier, label); } else if (type.equals("CHOICE")) { return new ChoiceFilter(key, identifier, label, choices); } else if (type.equals("MULTICHOICE")) { return new MultiChoiceFilter(key, identifier, label, choices); } else if (type.equals("STRING")) { return new TextFilter(key, identifier, label); } else if (type.equals("DEFAULT")) { final JSONObject fieldDef = instanceInfo.getFieldDefinitions().optJSONObject(defaultIdentifier); return new DefaultFilter(key, identifier, label, fieldDef); } else { throw new Exception("Invalid filter type defined in config: " + type); } } private void loadFilterDefinitions(JSONObject filterGroups) { loadFilterDef(filterGroups.optJSONArray("standard"), false); loadFilterDef(filterGroups.optJSONArray("missing"), true); } private void loadFilterDef(JSONArray filterDefs, Boolean isMissing) { if (filterDefs == null || filterDefs.length() == 0) { return; } String keyPrefix = isMissing ? "m:" : "s:"; for (int i = 0; i < filterDefs.length(); i++) { try { JSONObject def = filterDefs.getJSONObject(i); String identifier = def.getString("identifier"); String key = keyPrefix + identifier; String label = def.getString("label"); String type = def.optString("search_type"); if (isMissing) { type = "MISSING"; } JSONArray choices = def.optJSONArray("choices"); String defaultIdentifier = def.optString("default_identifier", identifier); BaseFilter filter = makeMapFilter(key, identifier, label, type, choices, defaultIdentifier); allFilters.put(key, filter); } catch (Exception e) { Logger.error("Could not create a filter from def # " + i, e); } } } /** * All species objects indexed by their id. */ public LinkedHashMap<Integer, Species> getSpecies() { return species; } public LinkedHashMap<String, BaseFilter> getFilters() { return allFilters; } /** * Returns a comma separated string of active filter names */ public String getActiveFilterDisplay() { String display = "", sep = ""; for (Map.Entry<String, BaseFilter> entry : allFilters.entrySet()) { BaseFilter filter = entry.getValue(); if (filter.isActive()) { display += sep + filter.label; sep = ", "; } } return display; } /** * Returns the active filters to serialize into a tiler query * @return a list of filter objects. Some filters have to be grouped together into JSONArrays, * the others are returned as JSONObjects */ public Collection<Either<JSONObject, JSONArray>> getActiveFilters() { final List<Either<JSONObject, JSONArray>> filterObjects = new ArrayList<>(); // Right now the only default filters are for Alerts search. // They need to be grouped into an "OR" group final JSONArray defaultFilters = new JSONArray(); defaultFilters.put("OR"); for (BaseFilter filter : allFilters.values()) { if (filter.isActive()) { final JSONObject filterObject = filter.getFilterObject(); if (filter instanceof DefaultFilter) { defaultFilters.put(filterObject); } else { filterObjects.add(Either.left(filterObject)); } } } if (defaultFilters.length() > 1) { filterObjects.add(Either.right(defaultFilters)); } return filterObjects; } }