Filter.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 30k
Category:

Windows Develop

Development Platform:

Java

  1. /*
  2.  *    This program is free software; you can redistribute it and/or modify
  3.  *    it under the terms of the GNU General Public License as published by
  4.  *    the Free Software Foundation; either version 2 of the License, or
  5.  *    (at your option) any later version.
  6.  *
  7.  *    This program is distributed in the hope that it will be useful,
  8.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.  *    GNU General Public License for more details.
  11.  *
  12.  *    You should have received a copy of the GNU General Public License
  13.  *    along with this program; if not, write to the Free Software
  14.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15.  */
  16. /*
  17.  *    Filter.java
  18.  *    Copyright (C) 1999 Len Trigg
  19.  *
  20.  */
  21. package weka.filters;
  22. import java.io.BufferedReader;
  23. import java.io.FileOutputStream;
  24. import java.io.FileReader;
  25. import java.io.InputStreamReader;
  26. import java.io.PrintWriter;
  27. import java.io.Reader;
  28. import java.io.Serializable;
  29. import java.util.Enumeration;
  30. import weka.core.Attribute;
  31. import weka.core.Instance;
  32. import weka.core.Instances;
  33. import weka.core.Option;
  34. import weka.core.OptionHandler;
  35. import weka.core.Queue;
  36. import weka.core.Utils;
  37. /** 
  38.  * An abstract class for instance filters: objects that take instances
  39.  * as input, carry out some transformation on the instance and then
  40.  * output the instance. The method implementations in this class
  41.  * assume that most of the work will be done in the methods overridden
  42.  * by subclasses.<p>
  43.  *
  44.  * A simple example of filter use. This example doesn't remove
  45.  * instances from the output queue until all instances have been
  46.  * input, so has higher memory consumption than an approach that
  47.  * uses output instances as they are made available:<p>
  48.  *
  49.  * <code> <pre>
  50.  *  Filter filter = ..some type of filter..
  51.  *  Instances instances = ..some instances..
  52.  *  for (int i = 0; i < data.numInstances(); i++) {
  53.  *    filter.input(data.instance(i));
  54.  *  }
  55.  *  filter.batchFinished();
  56.  *  Instances newData = filter.outputFormat();
  57.  *  Instance processed;
  58.  *  while ((processed = filter.output()) != null) {
  59.  *    newData.add(processed);
  60.  *  }
  61.  *  ..do something with newData..
  62.  * </pre> </code>
  63.  *
  64.  * @author Len Trigg (trigg@cs.waikato.ac.nz)
  65.  * @version $Revision: 1.21 $
  66.  */
  67. public abstract class Filter implements Serializable {
  68.   /*
  69.    * Filter refactoring TODO:
  70.    *
  71.    * - Update all filters to use getOutputFormat and setInputFormat
  72.    * instead of outputFormat, outputFormatPeek and inputFormat.
  73.    * - Update users of filters to use getOutputFormat and setInputFormat
  74.    * - remove outputFormat, outputFormatPeek and inputFormat
  75.    *
  76.    */
  77.   /** Debugging mode */
  78.   private boolean m_Debug = false;
  79.   /** The output format for instances */
  80.   private Instances m_OutputFormat = null;
  81.   /** The output instance queue */
  82.   private Queue m_OutputQueue = null;
  83.   /** Indices of string attributes in the output format */
  84.   private int [] m_OutputStringAtts = null;
  85.   /** Indices of string attributes in the input format */
  86.   private int [] m_InputStringAtts = null;
  87.   /** The input format for instances */
  88.   private Instances m_InputFormat = null;
  89.   /** Record whether the filter is at the start of a batch */
  90.   protected boolean m_NewBatch = true;
  91.   /**
  92.    * Sets the format of output instances. The derived class should use this
  93.    * method once it has determined the outputformat. The 
  94.    * output queue is cleared.
  95.    *
  96.    * @param outputFormat the new output format
  97.    */
  98.   protected void setOutputFormat(Instances outputFormat) {
  99.     if (outputFormat != null) {
  100.       m_OutputFormat = outputFormat.stringFreeStructure();
  101.       m_OutputStringAtts = getStringIndices(m_OutputFormat);
  102.       // Rename the attribute
  103.       String relationName = outputFormat.relationName() 
  104.         + "-" + this.getClass().getName();
  105.       if (this instanceof OptionHandler) {
  106.         String [] options = ((OptionHandler)this).getOptions();
  107.         for (int i = 0; i < options.length; i++) {
  108.           relationName += options[i].trim();
  109.         }
  110.       }
  111.       m_OutputFormat.setRelationName(relationName);
  112.     } else {
  113.       m_OutputFormat = null;
  114.     }
  115.     m_OutputQueue = new Queue();
  116.   }
  117.   /**
  118.    * Gets the currently set inputformat instances. This dataset may contain
  119.    * buffered instances.
  120.    *
  121.    * @return the input Instances.
  122.    */
  123.   protected Instances getInputFormat() {
  124.     return m_InputFormat;
  125.   }
  126.   /**
  127.    * Returns a reference to the current output format without
  128.    * copying it.
  129.    *
  130.    * @return a reference to the current output format
  131.    */
  132.   protected Instances outputFormatPeek() {
  133.     return m_OutputFormat;
  134.   }
  135.   /**
  136.    * Adds an output instance to the queue. The derived class should use this
  137.    * method for each output instance it makes available. 
  138.    *
  139.    * @param instance the instance to be added to the queue
  140.    */
  141.   protected void push(Instance instance) {
  142.     if (instance != null) {
  143.       copyStringValues(instance, m_OutputFormat, m_OutputStringAtts);
  144.       instance.setDataset(m_OutputFormat);
  145.       m_OutputQueue.push(instance);
  146.     }
  147.   }
  148.   /**
  149.    * Clears the output queue.
  150.    */
  151.   protected void resetQueue() {
  152.     m_OutputQueue = new Queue();
  153.   }
  154.   /**
  155.    * Adds the supplied input instance to the inputformat dataset for
  156.    * later processing.  Use this method rather than
  157.    * getInputFormat().add(instance). Or else.
  158.    *
  159.    * @param instance the <code>Instance</code> to buffer.  
  160.    */
  161.   protected void bufferInput(Instance instance) {
  162.     if (instance != null) {
  163.       copyStringValues(instance, m_InputFormat, m_InputStringAtts);
  164.       instance.setDataset(m_InputFormat);
  165.       m_InputFormat.add(instance);
  166.     }
  167.   }
  168.   /**
  169.    * Returns an array containing the indices of all string attributes in the
  170.    * input format. This index is created during setInputFormat()
  171.    *
  172.    * @return an array containing the indices of string attributes in the 
  173.    * input dataset.
  174.    */
  175.   protected int [] getInputStringIndex() {
  176.     return m_InputStringAtts;
  177.   }
  178.   /**
  179.    * Returns an array containing the indices of all string attributes in the
  180.    * output format. This index is created during setOutputFormat()
  181.    *
  182.    * @return an array containing the indices of string attributes in the 
  183.    * output dataset.
  184.    */
  185.   protected int [] getOutputStringIndex() {
  186.     return m_OutputStringAtts;
  187.   }
  188.   /**
  189.    * Copies string values contained in the instance copied to a new
  190.    * dataset. The Instance must already be assigned to a dataset. This
  191.    * dataset and the destination dataset must have the same structure.
  192.    *
  193.    * @param instance the Instance containing the string values to copy.
  194.    * @param destDataset the destination set of Instances
  195.    * @param strAtts an array containing the indices of any string attributes
  196.    * in the dataset.  
  197.    */
  198.   private void copyStringValues(Instance inst, Instances destDataset, 
  199.                                 int []strAtts) {
  200.     if (strAtts.length == 0) {
  201.       return;
  202.     }
  203.     if (inst.dataset() == null) {
  204.       throw new IllegalArgumentException("Instance has no dataset assigned!!");
  205.     } else if (inst.dataset().numAttributes() != destDataset.numAttributes()) {
  206.       throw new IllegalArgumentException("Src and Dest differ in # of attributes!!");
  207.     } 
  208.     copyStringValues(inst, true, inst.dataset(), strAtts,
  209.                      destDataset, strAtts);
  210.   }
  211.   /**
  212.    * Takes string values referenced by an Instance and copies them from a
  213.    * source dataset to a destination dataset. The instance references are
  214.    * updated to be valid for the destination dataset. The instance may have the 
  215.    * structure (i.e. number and attribute position) of either dataset (this
  216.    * affects where references are obtained from). The source dataset must
  217.    * have the same structure as the filter input format and the destination
  218.    * must have the same structure as the filter output format.
  219.    *
  220.    * @param instance the instance containing references to strings in the source
  221.    * dataset that will have references updated to be valid for the destination
  222.    * dataset.
  223.    * @param instSrcCompat true if the instance structure is the same as the
  224.    * source, or false if it is the same as the destination
  225.    * @param srcDataset the dataset for which the current instance string
  226.    * references are valid (after any position mapping if needed)
  227.    * @param destDataset the dataset for which the current instance string
  228.    * references need to be inserted (after any position mapping if needed)
  229.    */
  230.   protected void copyStringValues(Instance instance, boolean instSrcCompat,
  231.                                   Instances srcDataset, Instances destDataset) {
  232.     copyStringValues(instance, instSrcCompat, srcDataset, m_InputStringAtts,
  233.                      destDataset, m_OutputStringAtts);
  234.   }
  235.   /**
  236.    * Takes string values referenced by an Instance and copies them from a
  237.    * source dataset to a destination dataset. The instance references are
  238.    * updated to be valid for the destination dataset. The instance may have the 
  239.    * structure (i.e. number and attribute position) of either dataset (this
  240.    * affects where references are obtained from). Only works if the number
  241.    * of string attributes is the same in both indices (implicitly these string
  242.    * attributes should be semantically same but just with shifted positions).
  243.    *
  244.    * @param instance the instance containing references to strings in the source
  245.    * dataset that will have references updated to be valid for the destination
  246.    * dataset.
  247.    * @param instSrcCompat true if the instance structure is the same as the
  248.    * source, or false if it is the same as the destination (i.e. which of the
  249.    * string attribute indices contains the correct locations for this instance).
  250.    * @param srcDataset the dataset for which the current instance string
  251.    * references are valid (after any position mapping if needed)
  252.    * @param srcStrAtts an array containing the indices of string attributes
  253.    * in the source datset.
  254.    * @param destDataset the dataset for which the current instance string
  255.    * references need to be inserted (after any position mapping if needed)
  256.    * @param destStrAtts an array containing the indices of string attributes
  257.    * in the destination datset.
  258.    */
  259.   protected void copyStringValues(Instance instance, boolean instSrcCompat,
  260.                                   Instances srcDataset, int []srcStrAtts,
  261.                                   Instances destDataset, int []destStrAtts) {
  262.     if (srcDataset == destDataset) {
  263.       return;
  264.     }
  265.     if (srcStrAtts.length != destStrAtts.length) {
  266.       throw new IllegalArgumentException("Src and Dest string indices differ in length!!");
  267.     }
  268.     for (int i = 0; i < srcStrAtts.length; i++) {
  269.       int instIndex = instSrcCompat ? srcStrAtts[i] : destStrAtts[i];
  270.       Attribute src = srcDataset.attribute(srcStrAtts[i]);
  271.       Attribute dest = destDataset.attribute(destStrAtts[i]);
  272.       if (!instance.isMissing(instIndex)) {
  273.         //System.err.println(instance.value(srcIndex) 
  274.         //                   + " " + src.numValues()
  275.         //                   + " " + dest.numValues());
  276.         int valIndex = dest.addStringValue(src, (int)instance.value(instIndex));
  277.         // setValue here shouldn't be too slow here unless your dataset has
  278.         // squillions of string attributes
  279.         instance.setValue(instIndex, (double)valIndex);
  280.       }
  281.     }
  282.   }
  283.   /**
  284.    * This will remove all buffered instances from the inputformat dataset.
  285.    * Use this method rather than getInputFormat().delete();
  286.    */
  287.   protected void flushInput() {
  288.     if (m_InputStringAtts.length > 0) {
  289.       m_InputFormat = m_InputFormat.stringFreeStructure();
  290.     } else {
  291.       // This more efficient than new Instances(m_InputFormat, 0);
  292.       m_InputFormat.delete();
  293.     }
  294.   }
  295.   /**
  296.    * @deprecated use <code>setInputFormat(Instances)</code> instead.
  297.    */
  298.   public boolean inputFormat(Instances instanceInfo) throws Exception {
  299.     return setInputFormat(instanceInfo);
  300.   }
  301.   /**
  302.    * Sets the format of the input instances. If the filter is able to
  303.    * determine the output format before seeing any input instances, it
  304.    * does so here. This default implementation clears the output format
  305.    * and output queue, and the new batch flag is set. Overriders should
  306.    * call <code>super.setInputFormat(Instances)</code>
  307.    *
  308.    * @param instanceInfo an Instances object containing the input instance
  309.    * structure (any instances contained in the object are ignored - only the
  310.    * structure is required).
  311.    * @return true if the outputFormat may be collected immediately
  312.    * @exception Exception if the inputFormat can't be set successfully 
  313.    */
  314.   public boolean setInputFormat(Instances instanceInfo) throws Exception {
  315.     m_InputFormat = instanceInfo.stringFreeStructure();
  316.     m_InputStringAtts = getStringIndices(instanceInfo);
  317.     m_OutputFormat = null;
  318.     m_OutputQueue = new Queue();
  319.     m_NewBatch = true;
  320.     return false;
  321.   }
  322.   /**
  323.    * @deprecated use <code>getOutputFormat()</code> instead.
  324.    */
  325.   public final Instances outputFormat() {
  326.     return getOutputFormat();
  327.   }
  328.   /**
  329.    * Gets the format of the output instances. This should only be called
  330.    * after input() or batchFinished() has returned true. The relation
  331.    * name of the output instances should be changed to reflect the
  332.    * action of the filter (eg: add the filter name and options).
  333.    *
  334.    * @return an Instances object containing the output instance
  335.    * structure only.
  336.    * @exception NullPointerException if no input structure has been
  337.    * defined (or the output format hasn't been determined yet) 
  338.    */
  339.   public final Instances getOutputFormat() {
  340.     if (m_OutputFormat == null) {
  341.       throw new NullPointerException("No output format defined.");
  342.     }
  343.     return new Instances(m_OutputFormat, 0);
  344.   }
  345.   /**
  346.    * Input an instance for filtering. Ordinarily the instance is
  347.    * processed and made available for output immediately. Some filters
  348.    * require all instances be read before producing output, in which
  349.    * case output instances should be collected after calling
  350.    * batchFinished(). If the input marks the start of a new batch, the
  351.    * output queue is cleared. This default implementation assumes all
  352.    * instance conversion will occur when batchFinished() is called.
  353.    *
  354.    * @param instance the input instance
  355.    * @return true if the filtered instance may now be
  356.    * collected with output().
  357.    * @exception NullPointerException if the input format has not been
  358.    * defined.
  359.    * @exception Exception if the input instance was not of the correct 
  360.    * format or if there was a problem with the filtering.  
  361.    */
  362.   public boolean input(Instance instance) throws Exception {
  363.     if (m_InputFormat == null) {
  364.       throw new NullPointerException("No input instance format defined");
  365.     }
  366.     if (m_NewBatch) {
  367.       m_OutputQueue = new Queue();
  368.       m_NewBatch = false;
  369.     }
  370.     bufferInput(instance);
  371.     return false;
  372.   }
  373.   /**
  374.    * Signify that this batch of input to the filter is finished. If
  375.    * the filter requires all instances prior to filtering, output()
  376.    * may now be called to retrieve the filtered instances. Any
  377.    * subsequent instances filtered should be filtered based on setting
  378.    * obtained from the first batch (unless the inputFormat has been
  379.    * re-assigned or new options have been set). This default
  380.    * implementation assumes all instance processing occurs during
  381.    * inputFormat() and input().
  382.    *
  383.    * @return true if there are instances pending output
  384.    * @exception NullPointerException if no input structure has been defined,
  385.    * @exception Exception if there was a problem finishing the batch.
  386.    */
  387.   public boolean batchFinished() throws Exception {
  388.     if (m_InputFormat == null) {
  389.       throw new NullPointerException("No input instance format defined");
  390.     }
  391.     flushInput();
  392.     m_NewBatch = true;
  393.     return (numPendingOutput() != 0);
  394.   }
  395.   /**
  396.    * Output an instance after filtering and remove from the output queue.
  397.    *
  398.    * @return the instance that has most recently been filtered (or null if
  399.    * the queue is empty).
  400.    * @exception NullPointerException if no output structure has been defined
  401.    */
  402.   public Instance output() {
  403.     if (m_OutputFormat == null) {
  404.       throw new NullPointerException("No output instance format defined");
  405.     }
  406.     if (m_OutputQueue.empty()) {
  407.       return null;
  408.     }
  409.     Instance result = (Instance)m_OutputQueue.pop();
  410.     // Clear out references to old strings occasionally
  411.     if (m_OutputQueue.empty() && m_NewBatch) {
  412.       if (m_OutputStringAtts.length > 0) {
  413.         m_OutputFormat = m_OutputFormat.stringFreeStructure();
  414.       }
  415.     }
  416.     return result;
  417.   }
  418.   
  419.   /**
  420.    * Output an instance after filtering but do not remove from the
  421.    * output queue.
  422.    *
  423.    * @return the instance that has most recently been filtered (or null if
  424.    * the queue is empty).
  425.    * @exception NullPointerException if no input structure has been defined 
  426.    */
  427.   public Instance outputPeek() {
  428.     if (m_OutputFormat == null) {
  429.       throw new NullPointerException("No output instance format defined");
  430.     }
  431.     if (m_OutputQueue.empty()) {
  432.       return null;
  433.     }
  434.     Instance result = (Instance)m_OutputQueue.peek();
  435.     return result;
  436.   }
  437.   /**
  438.    * Returns the number of instances pending output
  439.    *
  440.    * @return the number of instances  pending output
  441.    * @exception NullPointerException if no input structure has been defined
  442.    */
  443.   public int numPendingOutput() {
  444.     if (m_OutputFormat == null) {
  445.       throw new NullPointerException("No output instance format defined");
  446.     }
  447.     return m_OutputQueue.size();
  448.   }
  449.   /**
  450.    * Returns whether the output format is ready to be collected
  451.    *
  452.    * @return true if the output format is set
  453.    */
  454.   public boolean isOutputFormatDefined() {
  455.     return (m_OutputFormat != null);
  456.   }
  457.   /**
  458.    * Gets an array containing the indices of all string attributes.
  459.    *
  460.    * @param insts the Instances to scan for string attributes. 
  461.    * @return an array containing the indices of string attributes in
  462.    * the input structure. Will be zero-length if there are no
  463.    * string attributes
  464.    */
  465.   protected int [] getStringIndices(Instances insts) {
  466.     
  467.     // Scan through getting the indices of String attributes
  468.     int [] index = new int [insts.numAttributes()];
  469.     int indexSize = 0;
  470.     for (int i = 0; i < insts.numAttributes(); i++) {
  471.       if (insts.attribute(i).type() == Attribute.STRING) {
  472.         index[indexSize++] = i;
  473.       }
  474.     }
  475.     int [] result = new int [indexSize];
  476.     System.arraycopy(index, 0, result, 0, indexSize);
  477.     return result;
  478.   }
  479.   
  480.   /**
  481.    * Filters an entire set of instances through a filter and returns
  482.    * the new set. 
  483.    *
  484.    * @param data the data to be filtered
  485.    * @param filter the filter to be used
  486.    * @return the filtered set of data
  487.    * @exception Exception if the filter can't be used successfully
  488.    */
  489.   public static Instances useFilter(Instances data,
  490.     Filter filter) throws Exception {
  491.     /*
  492.     System.err.println(filter.getClass().getName() 
  493.                        + " in:" + data.numInstances());
  494.     */
  495.     for (int i = 0; i < data.numInstances(); i++) {
  496.       filter.input(data.instance(i));
  497.     }
  498.     filter.batchFinished();
  499.     Instances newData = filter.getOutputFormat();
  500.     Instance processed;
  501.     while ((processed = filter.output()) != null) {
  502.       newData.add(processed);
  503.     }
  504.     /*
  505.     System.err.println(filter.getClass().getName() 
  506.                        + " out:" + newData.numInstances());
  507.     */
  508.     return newData;
  509.   }
  510.   /**
  511.    * Method for testing filters.
  512.    *
  513.    * @param argv should contain the following arguments: <br>
  514.    * -i input_file <br>
  515.    * -o output_file <br>
  516.    * -c class_index <br>
  517.    * or -h for help on options
  518.    * @exception Exception if something goes wrong or the user requests help on
  519.    * command options
  520.    */
  521.   public static void filterFile(Filter filter, String [] options) 
  522.     throws Exception {
  523.     boolean debug = false;
  524.     Instances data = null;
  525.     Reader input = null;
  526.     PrintWriter output = null;
  527.     boolean helpRequest;
  528.     try {
  529.        helpRequest = Utils.getFlag('h', options);
  530.       if (Utils.getFlag('d', options)) {
  531. debug = true;
  532.       }
  533.       String infileName = Utils.getOption('i', options);
  534.       String outfileName = Utils.getOption('o', options); 
  535.       String classIndex = Utils.getOption('c', options);
  536.       
  537.       if (filter instanceof OptionHandler) {
  538. ((OptionHandler)filter).setOptions(options);
  539.       }
  540.       Utils.checkForRemainingOptions(options);
  541.       if (helpRequest) {
  542. throw new Exception("Help requested.n");
  543.       }
  544.       if (infileName.length() != 0) {
  545. input = new BufferedReader(new FileReader(infileName));
  546.       } else {
  547. input = new BufferedReader(new InputStreamReader(System.in));
  548.       }
  549.       if (outfileName.length() != 0) {
  550. output = new PrintWriter(new FileOutputStream(outfileName));
  551.       } else { 
  552. output = new PrintWriter(System.out);
  553.       }
  554.       data = new Instances(input, 1);
  555.       if (classIndex.length() != 0) {
  556. if (classIndex.equals("first")) {
  557.   data.setClassIndex(0);
  558. } else if (classIndex.equals("last")) {
  559.   data.setClassIndex(data.numAttributes() - 1);
  560. } else {
  561.   data.setClassIndex(Integer.parseInt(classIndex) - 1);
  562. }
  563.       }
  564.     } catch (Exception ex) {
  565.       String filterOptions = "";
  566.       // Output the error and also the valid options
  567.       if (filter instanceof OptionHandler) {
  568. filterOptions += "nFilter options:nn";
  569. Enumeration enum = ((OptionHandler)filter).listOptions();
  570. while (enum.hasMoreElements()) {
  571.   Option option = (Option) enum.nextElement();
  572.   filterOptions += option.synopsis() + 'n'
  573.     + option.description() + "n";
  574. }
  575.       }
  576.       String genericOptions = "nGeneral options:nn"
  577. + "-hn"
  578. + "tGet help on available options.n"
  579. + "t(use -b -h for help on batch mode.)n"
  580. + "-i <file>n"
  581. + "tThe name of the file containing input instances.n"
  582. + "tIf not supplied then instances will be read from stdin.n"
  583. + "-o <file>n"
  584. + "tThe name of the file output instances will be written to.n"
  585. + "tIf not supplied then instances will be written to stdout.n"
  586. + "-c <class index>n"
  587. + "tThe number of the attribute to use as the class.n"
  588. + "t"first" and "last" are also valid entries.n"
  589. + "tIf not supplied then no class is assigned.n";
  590.       throw new Exception('n' + ex.getMessage()
  591.   + filterOptions+genericOptions);
  592.     }
  593.     
  594.     if (debug) {
  595.       System.err.println("Setting input format");
  596.     }
  597.     boolean printedHeader = false;
  598.     if (filter.setInputFormat(data)) {
  599.       if (debug) {
  600. System.err.println("Getting output format");
  601.       }
  602.       output.println(filter.getOutputFormat().toString());
  603.       printedHeader = true;
  604.     }
  605.     
  606.     // Pass all the instances to the filter
  607.     while (data.readInstance(input)) {
  608.       if (debug) {
  609. System.err.println("Input instance to filter");
  610.       }
  611.       if (filter.input(data.instance(0))) {
  612. if (debug) {
  613.   System.err.println("Filter said collect immediately");
  614. }
  615. if (!printedHeader) {
  616.   throw new Error("Filter didn't return true from setInputFormat() "
  617.   + "earlier!");
  618. }
  619. if (debug) {
  620.   System.err.println("Getting output instance");
  621. }
  622. output.println(filter.output().toString());
  623.       }
  624.       data.delete(0);
  625.     }
  626.     
  627.     // Say that input has finished, and print any pending output instances
  628.     if (debug) {
  629.       System.err.println("Setting end of batch");
  630.     }
  631.     if (filter.batchFinished()) {
  632.       if (debug) {
  633. System.err.println("Filter said collect output");
  634.       }
  635.       if (!printedHeader) {
  636. if (debug) {
  637.   System.err.println("Getting output format");
  638. }
  639. output.println(filter.getOutputFormat().toString());
  640.       }
  641.       if (debug) {
  642. System.err.println("Getting output instance");
  643.       }
  644.       while (filter.numPendingOutput() > 0) {
  645. output.println(filter.output().toString());
  646. if (debug){
  647.   System.err.println("Getting output instance");
  648. }
  649.       }
  650.     }
  651.     if (debug) {
  652.       System.err.println("Done");
  653.     }
  654.     
  655.     if (output != null) {
  656.       output.close();
  657.     }
  658.   }
  659.   /**
  660.    * Method for testing filters ability to process multiple batches.
  661.    *
  662.    * @param argv should contain the following arguments:<br>
  663.    * -i (first) input file <br>
  664.    * -o (first) output file <br>
  665.    * -r (second) input file <br>
  666.    * -s (second) output file <br>
  667.    * -c class_index <br>
  668.    * or -h for help on options
  669.    * @exception Exception if something goes wrong or the user requests help on
  670.    * command options
  671.    */
  672.   public static void batchFilterFile(Filter filter, String [] options) 
  673.     throws Exception {
  674.     Instances firstData = null;
  675.     Instances secondData = null;
  676.     Reader firstInput = null;
  677.     Reader secondInput = null;
  678.     PrintWriter firstOutput = null;
  679.     PrintWriter secondOutput = null;
  680.     boolean helpRequest;
  681.     try {
  682.       helpRequest = Utils.getFlag('h', options);
  683.       String fileName = Utils.getOption('i', options); 
  684.       if (fileName.length() != 0) {
  685. firstInput = new BufferedReader(new FileReader(fileName));
  686.       } else {
  687. throw new Exception("No first input file given.n");
  688.       }
  689.       fileName = Utils.getOption('r', options); 
  690.       if (fileName.length() != 0) {
  691. secondInput = new BufferedReader(new FileReader(fileName));
  692.       } else {
  693. throw new Exception("No second input file given.n");
  694.       }
  695.       fileName = Utils.getOption('o', options); 
  696.       if (fileName.length() != 0) {
  697. firstOutput = new PrintWriter(new FileOutputStream(fileName));
  698.       } else {
  699. firstOutput = new PrintWriter(System.out);
  700.       }
  701.       
  702.       fileName = Utils.getOption('s', options); 
  703.       if (fileName.length() != 0) {
  704. secondOutput = new PrintWriter(new FileOutputStream(fileName));
  705.       } else {
  706. secondOutput = new PrintWriter(System.out);
  707.       }
  708.       String classIndex = Utils.getOption('c', options);
  709.       if (filter instanceof OptionHandler) {
  710. ((OptionHandler)filter).setOptions(options);
  711.       }
  712.       Utils.checkForRemainingOptions(options);
  713.       
  714.       if (helpRequest) {
  715. throw new Exception("Help requested.n");
  716.       }
  717.       firstData = new Instances(firstInput, 1);
  718.       secondData = new Instances(secondInput, 1);
  719.       if (!secondData.equalHeaders(firstData)) {
  720. throw new Exception("Input file formats differ.n");
  721.       }
  722.       if (classIndex.length() != 0) {
  723. if (classIndex.equals("first")) {
  724.   firstData.setClassIndex(0);
  725.   secondData.setClassIndex(0);
  726. } else if (classIndex.equals("last")) {
  727.   firstData.setClassIndex(firstData.numAttributes() - 1);
  728.   secondData.setClassIndex(secondData.numAttributes() - 1);
  729. } else {
  730.   firstData.setClassIndex(Integer.parseInt(classIndex) - 1);
  731.   secondData.setClassIndex(Integer.parseInt(classIndex) - 1);
  732. }
  733.       }
  734.     } catch (Exception ex) {
  735.       String filterOptions = "";
  736.       // Output the error and also the valid options
  737.       if (filter instanceof OptionHandler) {
  738. filterOptions += "nFilter options:nn";
  739. Enumeration enum = ((OptionHandler)filter).listOptions();
  740. while (enum.hasMoreElements()) {
  741.   Option option = (Option) enum.nextElement();
  742.   filterOptions += option.synopsis() + 'n'
  743.     + option.description() + "n";
  744. }
  745.       }
  746.       String genericOptions = "nGeneral options:nn"
  747. + "-hn"
  748. + "tGet help on available options.n"
  749. + "-i <filename>n"
  750. + "tThe file containing first input instances.n"
  751. + "-o <filename>n"
  752. + "tThe file first output instances will be written to.n"
  753. + "-r <filename>n"
  754. + "tThe file containing second input instances.n"
  755. + "-s <filename>n"
  756. + "tThe file second output instances will be written to.n"
  757. + "-c <class index>n"
  758. + "tThe number of the attribute to use as the class.n"
  759. + "t"first" and "last" are also valid entries.n"
  760. + "tIf not supplied then no class is assigned.n";
  761.       throw new Exception('n' + ex.getMessage()
  762.   + filterOptions+genericOptions);
  763.     }
  764.     boolean printedHeader = false;
  765.     if (filter.setInputFormat(firstData)) {
  766.       firstOutput.println(filter.getOutputFormat().toString());
  767.       printedHeader = true;
  768.     }
  769.     
  770.     // Pass all the instances to the filter
  771.     while (firstData.readInstance(firstInput)) {
  772.       if (filter.input(firstData.instance(0))) {
  773. if (!printedHeader) {
  774.   throw new Error("Filter didn't return true from setInputFormat() "
  775.   + "earlier!");
  776. }
  777. firstOutput.println(filter.output().toString());
  778.       }
  779.       firstData.delete(0);
  780.     }
  781.     
  782.     // Say that input has finished, and print any pending output instances
  783.     if (filter.batchFinished()) {
  784.       if (!printedHeader) {
  785. firstOutput.println(filter.getOutputFormat().toString());
  786.       }
  787.       while (filter.numPendingOutput() > 0) {
  788. firstOutput.println(filter.output().toString());
  789.       }
  790.     }
  791.     
  792.     if (firstOutput != null) {
  793.       firstOutput.close();
  794.     }    
  795.     printedHeader = false;
  796.     if (filter.isOutputFormatDefined()) {
  797.       secondOutput.println(filter.getOutputFormat().toString());
  798.       printedHeader = true;
  799.     }
  800.     // Pass all the second instances to the filter
  801.     while (secondData.readInstance(secondInput)) {
  802.       if (filter.input(secondData.instance(0))) {
  803. if (!printedHeader) {
  804.   throw new Error("Filter didn't return true from"
  805.   + " isOutputFormatDefined() earlier!");
  806. }
  807. secondOutput.println(filter.output().toString());
  808.       }
  809.       secondData.delete(0);
  810.     }
  811.     
  812.     // Say that input has finished, and print any pending output instances
  813.     if (filter.batchFinished()) {
  814.       if (!printedHeader) {
  815. secondOutput.println(filter.getOutputFormat().toString());
  816.       }
  817.       while (filter.numPendingOutput() > 0) {
  818. secondOutput.println(filter.output().toString());
  819.       }
  820.     }
  821.     if (secondOutput != null) {
  822.       secondOutput.close();
  823.     }
  824.   }
  825.   /**
  826.    * Main method for testing this class.
  827.    *
  828.    * @param argv should contain arguments to the filter: use -h for help
  829.    */
  830.   public static void main(String [] args) {
  831.     
  832.     try {
  833.       if (args.length == 0) {
  834.         throw new Exception("First argument must be the class name of a Filter");
  835.       }
  836.       String fname = args[0];
  837.       Filter f = (Filter)Class.forName(fname).newInstance();
  838.       args[0] = "";
  839.       if (Utils.getFlag('b', args)) {
  840. Filter.batchFilterFile(f, args);
  841.       } else {
  842. Filter.filterFile(f, args);
  843.       }
  844.     } catch (Exception ex) {
  845.       ex.printStackTrace();
  846.       System.out.println(ex.getMessage());
  847.     }
  848.   }
  849. }