ObservationFilter.java
/**
* VStar: a statistical analysis tool for variable star data.
* Copyright (C) 2010 AAVSO (http://www.aavso.org/)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.aavso.tools.vstar.data.filter;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.aavso.tools.vstar.data.ValidObservation;
import org.aavso.tools.vstar.util.Logic;
/**
* A filter for valid observations.
*
* An observation will be passed via this filter if the conjunction of all its
* sub-filters (field matchers) match the observation.
*/
public class ObservationFilter {
// Potential matchers for any observation filter.
public static final Map<String, IObservationFieldMatcher> MATCHERS;
static {
MATCHERS = new TreeMap<String, IObservationFieldMatcher>();
IObservationFieldMatcher objNameMatcher = new ObjectNameFieldMatcher();
MATCHERS.put(objNameMatcher.getDisplayName(), objNameMatcher);
IObservationFieldMatcher obsCodeMatcher = new ObsCodeFieldMatcher();
MATCHERS.put(obsCodeMatcher.getDisplayName(), obsCodeMatcher);
IObservationFieldMatcher magnitudeMatcher = new MagnitudeFieldMatcher();
MATCHERS.put(magnitudeMatcher.getDisplayName(), magnitudeMatcher);
IObservationFieldMatcher uncertaintyMatcher = new ErrorFieldMatcher();
MATCHERS.put(uncertaintyMatcher.getDisplayName(), uncertaintyMatcher);
IObservationFieldMatcher jdMatcher = new JDFieldMatcher();
MATCHERS.put(jdMatcher.getDisplayName(), jdMatcher);
IObservationFieldMatcher phaseMatcher = new PhaseFieldMatcher();
MATCHERS.put(phaseMatcher.getDisplayName(), phaseMatcher);
IObservationFieldMatcher transformedMatcher = new TransformedFieldMatcher();
MATCHERS.put(transformedMatcher.getDisplayName(), transformedMatcher);
IObservationFieldMatcher bandMatcher = new SeriesTypeFieldMatcher(SeriesTypeFieldMatcher.Kind.BAND);
MATCHERS.put(bandMatcher.getDisplayName(), bandMatcher);
IObservationFieldMatcher seriesMatcher = new SeriesTypeFieldMatcher(SeriesTypeFieldMatcher.Kind.SERIES);
MATCHERS.put(seriesMatcher.getDisplayName(), seriesMatcher);
}
// Actual matchers for this observation filter instance.
private List<IObservationFieldMatcher> matchers;
public ObservationFilter() {
matchers = new ArrayList<IObservationFieldMatcher>();
}
public void addMatcher(IObservationFieldMatcher matcher) {
matchers.add(matcher);
}
/**
* @return the matchers
*/
public List<IObservationFieldMatcher> getMatchers() {
return matchers;
}
/**
* Reset this filter's state.
*/
public void reset() {
matchers.clear();
}
/**
* Filter the supplied list of observations.
*
* @param obs
* The observation list to be filtered.
* @param includeFainterThan
* Should fainter-than observations be included?
* @param includeDiscrepant
* Should discrepant observations be included?
* @param includeExcluded
* Should excluded observations be included?
* @return The ordered (by insertion) set of filtered observations.
*/
public Set<ValidObservation> getFilteredObservations(
List<ValidObservation> obs, boolean includeFainterThan,
boolean includeDiscrepant, boolean includeExcluded) {
// We use a LinkedHashSet to maintain addition and lookup efficiency
// while maintaining insertion order.
// Note: this is only necessary if a recipient of filtered observations
// needs to perform lookups.
Set<ValidObservation> matchingObs = new LinkedHashSet<ValidObservation>();
for (int i = 0; i < obs.size(); i++) {
ValidObservation ob = obs.get(i);
boolean does_match = matches(ob);
if (does_match) {
/**
* Use logical implication, p => q, where p is the observation's
* property and q is the inclusion property relating to p, to
* check that our inclusion criteria still permit a match for
* this observation.
*/
does_match &= Logic.imp(ob.getMagnitude().isFainterThan(),
includeFainterThan);
does_match &= Logic.imp(ob.isDiscrepant(), includeDiscrepant);
does_match &= Logic.imp(ob.isExcluded(), includeExcluded);
if (does_match) {
matchingObs.add(ob);
}
}
}
return matchingObs;
}
/**
* Does the specified observation satisfy all this filter's matchers? The
* matching process is short-circuited when one sub-filter fails.
*
* @param ob
* The observation under test.
* @return True or false.
* @precondition It only makes sense to call this method when 'matchers' is
* non-empty, otherwise all observations will be filtered in.
*/
protected boolean matches(ValidObservation ob) {
boolean matching = true;
for (IObservationFieldMatcher matcher : matchers) {
matching &= matcher.matches(ob);
if (!matching)
break;
}
return matching;
}
}