package com.srbenoit.filter;
import java.io.File;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import com.srbenoit.log.LoggedObject;
/**
* The base class for a filter. Filters take one or more inputs (each in a specified data format)
* and produce outputs (again, in specified data formats). Filters draw their input data from a
* Pipe
, and may add their output data back to that Pipe
for downstream
* filters to draw from.
*/
public abstract class AbstractFilter extends LoggedObject {
/** version number for serialization */
private static final long serialVersionUID = -5289023315832548170L;
/** a zero-length class array to use when finding constructors */
private static final Class>[] CLASS_0;
/** a zero-length array of objects to facilitate array construction */
private static final Object[] OBJECT_0;
/** a zero-length array of Strings to facilitate array construction */
private static final String[] STRING_0;
/** the file extension for TXT files */
public final static String TXT_EXT = ".txt";
/** the filename of the report file indicating step completion */
public final static String REPORT = "report" + TXT_EXT;
/** the human-friendly name of the filter */
private final String name;
/** the XML tag of the filter */
private final String tag;
/** a set of properties that can configure the filter */
private final Properties properties;
/** the current state of the filter */
private FilterState state;
/** the selection state of the filter */
private boolean selected;
/** the provisional state of the filter */
private boolean provisional;
/** the list of formats of required input data */
protected final List inputs;
/** the list of the formats of output data */
protected final List outputs;
/** the renderer for the filter */
private FilterRenderer renderer;
static {
CLASS_0 = new Class>[0];
OBJECT_0 = new Object[0];
STRING_0 = new String[0];
}
/**
* Constructs a new AbstractFilter
.
*
* @param filterName the name of the filter
* @param filterTag the XML tag of the filter
*/
public AbstractFilter(final String filterName, final String filterTag) {
super();
this.name = filterName;
this.tag = filterTag;
this.properties = new Properties();
this.inputs = new ArrayList(5);
this.outputs = new ArrayList(5);
this.state = FilterState.INERT;
this.provisional = false;
this.selected = false;
}
/**
* Builds the renderer (must be called after inputs and outputs are configured).
*/
protected void makeRenderer() {
this.renderer = new FilterRenderer(this);
}
/**
* Gets the renderer that will draw the font.
*
* @return the renderer
*/
public FilterRenderer getRenderer() {
return this.renderer;
}
/**
* Gets the no-argument constructor for a filter class.
*
* @param clazz the filter class
* @return the no-argument constructor for instances of that filter
*/
public static Constructor extends AbstractFilter> getNoArgConstructor(
final Class extends AbstractFilter> clazz) {
Constructor extends AbstractFilter> constr;
try {
constr = clazz.getConstructor(CLASS_0);
} catch (Exception e) {
LOG.log(Level.WARNING,
"Exception while getting filter constructor for '" + clazz.getName() + "'", e);
constr = null;
}
return constr;
}
/**
* Gets a new instance of a filter based on its class.
*
* @param clazz the filter class
* @return the new instance
*/
public static AbstractFilter getInstance(final Class extends AbstractFilter> clazz) {
Constructor extends AbstractFilter> constr;
AbstractFilter instance;
try {
constr = clazz.getConstructor(CLASS_0);
instance = constr.newInstance(OBJECT_0);
} catch (Exception e) {
LOG.log(Level.WARNING, "Exception while getting instance of '" + clazz.getName() + "'",
e);
instance = null;
}
return instance;
}
/**
* Gets the filter name.
*
* @return the filter name
*/
public String getName() {
return this.name;
}
/**
* Gets the XML tag for the filter.
*
* @return the XML tag
*/
public String getTag() {
return this.tag;
}
/**
* Sets the value of a property.
*
* @param key the property key
* @param value the value to set
*/
public void setProperty(final String key, final String value) {
this.properties.setProperty(key, value);
}
/**
* Gets the value of a property.
*
* @param key the property key
* @return the value, or null
if the value has not been set
*/
public String getProperty(final String key) {
return this.properties.getProperty(key);
}
/**
* Gets the keys for all properties in the filter.
*
* @return the list of keys
*/
public String[] getPropertyKeys() {
return this.properties.keySet().toArray(STRING_0);
}
/**
* Gets the list of keys for all properties supported by the filter
*
* @return the list of keys
*/
public String[] getSupportedPropertyKeys() {
return new String[0];
}
/**
* Gets the number of input data formats required by this filter.
*
* @return the number of input data objects
*/
public int getNumInputs() {
return this.inputs.size();
}
/**
* Gets the number of output data formats generated by this filter (assuming only the required
* inputs are provided - non-required inputs may be passed through to the output, increasing
* their number).
*
* @return the number of output data objects
*/
public int getNumOutputs() {
return this.outputs.size();
}
/**
* Gets an input data format required by this filter.
*
* @param index the index of the format to retrieve
* @return the input format
*/
public FilterInput getInputFormat(final int index) {
return this.inputs.get(index);
}
/**
* Gets an output data format generated by this filter.
*
* @param index the index of the format to retrieve
* @return the output format
*/
public FilterOutput getOutputFormat(final int index) {
return this.outputs.get(index);
}
/**
* Duplicates the filter including all of its settings, but returns an independent object.
*
* @return the duplicated object
*/
public abstract AbstractFilter duplicate();
/**
* Performs the filter operation.
*
* @param executor the FilterTreeExecutor
that is executing the filter
* @param pipe the pipe from which to draw input data items and to which to add output
* data items
* @throws FilterException if the filter cannot complete
*/
public abstract void filter(FilterTreeExecutor executor, Pipe pipe) throws FilterException;
/**
* Validates that the pipe contains all required input data items.
*
* @param pipe the pipe containing the input data items to validate
* @throws FilterException if the inputs are invalid
*/
protected void validateInputs(final Pipe pipe) throws FilterException {
AbstractPipeItem item;
if (pipe == null) {
throw new FilterException("Pipe passed to validateInputs must not be null");
} else {
// Scan our required inputs testing for presence if each in the pipe
for (FilterInput input : this.inputs) {
item = pipe.get(input.getKey());
if (item == null) {
throw new FilterException("Input data did not contain a '"
+ input.type.getSimpleName() + "' input named '" + input.getKey() + "'");
}
if (!item.getClass().equals(input.type)) {
throw new FilterException("Input data did not contain a '"
+ input.type.getSimpleName() + "' input named '" + input.getKey() + "'");
}
}
}
}
/**
* Tests whether another object is equal to this one. To be equal, the other object must be an
* AbstractObject
, must have the same tag and name and the superclass, inputs,
* outputs, and children list objects must all be equal.
*
* @param obj the object to test
* @return true
if the object is equal to this object
*/
@Override public boolean equals(final Object obj) {
AbstractFilter filter;
boolean equal;
if (obj instanceof AbstractFilter) {
filter = (AbstractFilter) obj;
equal = super.equals(obj) && this.name.equals(filter.getName())
&& this.tag.equals(filter.getTag()) && this.inputs.equals(filter.inputs)
&& this.outputs.equals(filter.outputs);
} else {
equal = false;
}
return equal;
}
/**
* Gets the hash code for this object.
*
* @return the hash code
*/
@Override public int hashCode() {
return super.hashCode() + this.name.hashCode() + this.tag.hashCode()
+ this.inputs.hashCode() + this.outputs.hashCode();
}
/**
* Tests whether a completion report exists in the target directory.
*
* @param dir the directory in which to look for the report
* @return true
if the report was present; false
if not
*/
protected boolean testForCompletionReport(final File dir) {
return new File(dir, REPORT).exists();
}
/**
* Sets the state of the filter.
*
* @param newState the new state
*/
public void setState(final FilterState newState) {
this.state = newState;
}
/**
* Gets the state of the filter.
*
* @return the state
*/
public FilterState getState() {
return this.state;
}
/**
* Sets the selection state of the filter.
*
* @param isSelected true
if the filter is selected; false
if not
*/
public void setSelected(final boolean isSelected) {
this.selected = isSelected;
}
/**
* Gets the selection state of the filter.
*
* @return true
if the filter is selected; false
if not
*/
public boolean isSelected() {
return this.selected;
}
/**
* Sets the provisional state of the filter.
*
* @param isProvisional true
if the filter is provisional; false
if
* not
*/
public void setProvisional(final boolean isProvisional) {
this.provisional = isProvisional;
}
/**
* Gets the provisional state of the filter.
*
* @return true
if the filter is provisional; false
if not
*/
public boolean isProvisional() {
return this.provisional;
}
}