/**
* DataCleaner (community edition)
* Copyright (C) 2014 Neopost - Customer Information Management
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.datacleaner.job.builder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import org.datacleaner.api.Filter;
import org.datacleaner.descriptors.FilterDescriptor;
import org.datacleaner.job.AnalysisJobImmutabilizer;
import org.datacleaner.job.ComponentRequirement;
import org.datacleaner.job.FilterJob;
import org.datacleaner.job.FilterOutcome;
import org.datacleaner.job.HasFilterOutcomes;
import org.datacleaner.job.ImmutableComponentConfiguration;
import org.datacleaner.job.ImmutableFilterJob;
import org.datacleaner.job.OutputDataStreamJob;
/**
* A {@link ComponentBuilder} for {@link Filter}s.
*
* @param <F>
* the type of {@link Filter} being built
* @param <C>
* the {@link Filter}s category enum
*/
public final class FilterComponentBuilder<F extends Filter<C>, C extends Enum<C>>
extends AbstractComponentBuilder<FilterDescriptor<F, C>, F, FilterComponentBuilder<F, C>>
implements HasFilterOutcomes {
private final List<FilterChangeListener> _localChangeListeners;
// We keep a cached version of the resulting filter job because of
// references coming from other objects, particular LazyFilterOutcome.
private FilterJob _cachedJob;
private EnumMap<C, FilterOutcome> _outcomes;
public FilterComponentBuilder(final AnalysisJobBuilder analysisJobBuilder,
final FilterDescriptor<F, C> descriptor) {
super(analysisJobBuilder, descriptor, FilterComponentBuilder.class);
_outcomes = new EnumMap<>(descriptor.getOutcomeCategoryEnum());
final EnumSet<C> categories = descriptor.getOutcomeCategories();
for (final C category : categories) {
_outcomes.put(category, new LazyFilterOutcome(this, category));
}
_localChangeListeners = new ArrayList<>(0);
}
public FilterJob toFilterJob() {
return toFilterJob(true);
}
public FilterJob toFilterJob(final AnalysisJobImmutabilizer immutabilizer) {
return toFilterJob(true, immutabilizer);
}
public FilterJob toFilterJob(final boolean validate) {
return toFilterJob(validate, new AnalysisJobImmutabilizer());
}
public FilterJob toFilterJob(final boolean validate, final AnalysisJobImmutabilizer immutabilizer) {
if (validate && !isConfigured(true)) {
throw new IllegalStateException("Filter job is not correctly configured");
}
final ComponentRequirement componentRequirement = immutabilizer.load(getComponentRequirement());
final OutputDataStreamJob[] outputDataStreamJobs = immutabilizer.load(getOutputDataStreamJobs(), validate);
if (_cachedJob == null) {
_cachedJob = new ImmutableFilterJob(getName(), getDescriptor(),
new ImmutableComponentConfiguration(getConfiguredProperties()), componentRequirement,
getMetadataProperties(), outputDataStreamJobs);
} else {
final ImmutableFilterJob newFilterJob = new ImmutableFilterJob(getName(), getDescriptor(),
new ImmutableComponentConfiguration(getConfiguredProperties()), componentRequirement,
getMetadataProperties(), outputDataStreamJobs);
if (!newFilterJob.equals(_cachedJob)) {
_cachedJob = newFilterJob;
}
}
return _cachedJob;
}
/**
* Builds a temporary list of all listeners, both global and local
*
* @return
*/
private List<FilterChangeListener> getAllListeners() {
@SuppressWarnings("deprecation") final List<FilterChangeListener> globalChangeListeners =
getAnalysisJobBuilder().getFilterChangeListeners();
final List<FilterChangeListener> list =
new ArrayList<>(globalChangeListeners.size() + _localChangeListeners.size());
list.addAll(globalChangeListeners);
list.addAll(_localChangeListeners);
return list;
}
@Override
public String toString() {
return "FilterComponentBuilder[filter=" + getDescriptor().getDisplayName() + ",inputColumns="
+ getInputColumns() + "]";
}
@Override
public void onConfigurationChanged() {
super.onConfigurationChanged();
final List<FilterChangeListener> listeners = getAllListeners();
for (final FilterChangeListener listener : listeners) {
listener.onConfigurationChanged(this);
}
}
@Override
public void onRequirementChanged() {
super.onRequirementChanged();
final List<FilterChangeListener> listeners = getAllListeners();
for (final FilterChangeListener listener : listeners) {
listener.onRequirementChanged(this);
}
}
@Override
public Collection<FilterOutcome> getFilterOutcomes() {
return _outcomes.values();
}
/**
* @deprecated use {@link #getFilterOutcome(Enum)} instead
*/
@Deprecated
public FilterOutcome getOutcome(final C category) {
return getFilterOutcome(category);
}
public FilterOutcome getFilterOutcome(final C category) {
final FilterOutcome outcome = _outcomes.get(category);
if (outcome == null) {
throw new IllegalArgumentException(category + " is not a valid category for " + this);
}
return outcome;
}
/**
* @deprecated use {@link #getFilterOutcome(Object)} instead
*/
@Deprecated
public FilterOutcome getOutcome(final Object category) {
return getFilterOutcome(category);
}
public FilterOutcome getFilterOutcome(Object category) {
if (category instanceof String) {
final EnumSet<?> categories = getDescriptor().getOutcomeCategories();
for (final Enum<?> c : categories) {
if (c.name().equals(category)) {
category = c;
break;
}
}
}
final FilterOutcome outcome = _outcomes.get(category);
if (outcome == null) {
throw new IllegalArgumentException(category + " is not a valid category for " + this);
}
return outcome;
}
@Override
protected void onRemovedInternal() {
final List<FilterChangeListener> listeners = getAllListeners();
for (final FilterChangeListener listener : listeners) {
listener.onRemove(this);
}
}
/**
* Adds a change listener to this component
*
* @param listener
*/
public void addChangeListener(final FilterChangeListener listener) {
_localChangeListeners.add(listener);
}
/**
* Removes a change listener from this component
*
* @param listener
* @return whether or not the listener was found and removed.
*/
public boolean removeChangeListener(final FilterChangeListener listener) {
return _localChangeListeners.remove(listener);
}
}