text:
/*
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 *    Filter.java
 *    Copyright (C) 1999-2012 University of Waikato, Hamilton, New Zealand
 *
 */

package weka.filters;

import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;

import weka.core.Capabilities;
import weka.core.Capabilities.Capability;
import weka.core.CapabilitiesHandler;
import weka.core.CapabilitiesIgnorer;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.Option;
import weka.core.OptionHandler;
import weka.core.Queue;
import weka.core.RelationalLocator;
import weka.core.RevisionHandler;
import weka.core.RevisionUtils;
import weka.core.SerializedObject;
import weka.core.StringLocator;
import weka.core.UnsupportedAttributeTypeException;
import weka.core.Utils;
import weka.core.Version;
import weka.core.converters.ConverterUtils.DataSource;

/**
 * An abstract class for instance filters: objects that take instances as input,
 * carry out some transformation on the instance and then output the instance.
 * The method implementations in this class assume that most of the work will be
 * done in the methods overridden by subclasses.
 *

 *
 * A simple example of filter use. This example doesn't remove instances from
 * the output queue until all instances have been input, so has higher memory
 * consumption than an approach that uses output instances as they are made
 * available:
 *

 *
 *

 *  Filter filter = ..some type of filter..
 *  Instances instances = ..some instances..
 *  for (int i = 0; i < data.numInstances(); i++) {
 *    filter.input(data.instance(i));
 *  }
 *  filter.batchFinished();
 *  Instances newData = filter.outputFormat();
 *  Instance processed;
 *  while ((processed = filter.output()) != null) {
 *    newData.add(processed);
 *  }
 *  ..do something with newData..
 *

 *
 * @author Len Trigg (trigg@cs.waikato.ac.nz)
 * @version $Revision: 11006 $
 */
public abstract class Filter implements Serializable, CapabilitiesHandler,
                                        RevisionHandler, OptionHandler,
                                        CapabilitiesIgnorer {

  /** for serialization */
  private static final long serialVersionUID = -8835063755891851218L;

  /** The output format for instances */
  private Instances m_OutputFormat = null;

  /** The output instance queue */
  private Queue m_OutputQueue = null;

  /** Indices of string attributes in the output format */
  protected StringLocator m_OutputStringAtts = null;

  /** Indices of string attributes in the input format */
  protected StringLocator m_InputStringAtts = null;

  /** Indices of relational attributes in the output format */
  protected RelationalLocator m_OutputRelAtts = null;

  /** Indices of relational attributes in the input format */
  protected RelationalLocator m_InputRelAtts = null;

  /** The input format for instances */
  private Instances m_InputFormat = null;

  /** Record whether the filter is at the start of a batch */
  protected boolean m_NewBatch = true;

  /** True if the first batch has been done */
  protected boolean m_FirstBatchDone = false;

  /** Whether the classifier is run in debug mode. */
  protected boolean m_Debug = false;

  /** Whether capabilities should not be checked before classifier is built. */
  protected boolean m_DoNotCheckCapabilities = false;

  /**
   * Returns true if the a new batch was started, either a new instance of the
   * filter was created or the batchFinished() method got called.
   *
   * @return true if a new batch has been initiated
   * @see #m_NewBatch
   * @see #batchFinished()
   */
  public boolean isNewBatch() {
    return m_NewBatch;
  }

  /**
   * Returns true if the first batch of instances got processed. Necessary for
   * supervised filters, which "learn" from the first batch and then shouldn't
   * get updated with subsequent calls of batchFinished().
   *
   * @return true if the first batch has been processed
   * @see #m_FirstBatchDone
   * @see #batchFinished()
   */
  public boolean isFirstBatchDone() {
    return m_FirstBatchDone;
  }

  /**
   * Default implementation returns false. Some filters may not necessarily be
   * able to produce an instance for output for every instance input after the
   * first batch has been completed - such filters should override this method
   * and return true.
   *
   * @return false by default
   */
  public boolean mayRemoveInstanceAfterFirstBatchDone() {
    return false;
  }

  /**
   * Returns the Capabilities of this filter. Derived filters have to override
   * this method to enable capabilities.
   *
   * @return the capabilities of this object
   * @see Capabilities
   */
  @Override
  public Capabilities getCapabilities() {
    Capabilities result;

    result = new Capabilities(this);
    result.enableAll();

    result.setMinimumNumberInstances(0);

    return result;
  }

  /**
   * Returns the revision string.
   *
   * @return the revision
   */
  @Override
  public String getRevision() {
    return RevisionUtils.extract("$Revision: 11006 $");
  }

  /**
   * Returns the Capabilities of this filter, customized based on the data.
   * I.e., if removes all class capabilities, in case there's not class
   * attribute present or removes the NO_CLASS capability, in case that there's
   * a class present.
   *
   * @param data the data to use for customization
   * @return the capabilities of this object, based on the data
   * @see #getCapabilities()
   */
  public Capabilities getCapabilities(Instances data) {
    Capabilities result;
    Capabilities classes;
    Iterator<Capability> iter;
    Capability cap;

    result = getCapabilities();

    // no class? -> remove all class capabilites apart from NO_CLASS
    if (data.classIndex() == -1) {
      classes = result.getClassCapabilities();
      iter = classes.capabilities();
      while (iter.hasNext()) {
        cap = iter.next();
        if (cap != Capability.NO_CLASS) {
          result.disable(cap);
          result.disableDependency(cap);
        }
      }
    }
    // class? -> remove NO_CLASS
    else {
      result.disable(Capability.NO_CLASS);
      result.disableDependency(Capability.NO_CLASS);
    }

    return result;
  }

  /**
   * Sets the format of output instances. The derived class should use this
   * method once it has determined the outputformat. The output queue is
   * cleared.
   *
   * @param outputFormat the new output format
   */
  protected void setOutputFormat(Instances outputFormat) {

    if (outputFormat != null) {
      m_OutputFormat = outputFormat.stringFreeStructure();
      initOutputLocators(m_OutputFormat, null);

      // Rename the relation
      String relationName = outputFormat.relationName() + "-"
        + this.getClass().getName();
      if (this instanceof OptionHandler) {
        String[] options = ((OptionHandler) this).getOptions();
        for (String option : options) {
          relationName += option.trim();
        }
      }
      m_OutputFormat.setRelationName(relationName);
    } else {
      m_OutputFormat = null;
    }
    m_OutputQueue = new Queue();
  }

  /**
   * Gets the currently set inputformat instances. This dataset may contain
   * buffered instances.
   *
   * @return the input Instances.
   */
  protected Instances getInputFormat() {

    return m_InputFormat;
  }

  /**
   * Returns a reference to the current input format without copying it.
   *
   * @return a reference to the current input format
   */
  protected Instances inputFormatPeek() {

    return m_InputFormat;
  }

  /**
   * Returns a reference to the current output format without copying it.
   *
   * @return a reference to the current output format
   */
  protected Instances outputFormatPeek() {

    return m_OutputFormat;
  }

  /**
   * Adds an output instance to the queue. The derived class should use this
   * method for each output instance it makes available.
   *
   * @param instance the instance to be added to the queue.
   */
  protected void push(Instance instance) {

    if (instance != null) {
      if (instance.dataset() != null) {
        instance = (Instance) instance.copy(); // Don't modify incoming
        // instance!
        copyValues(instance, false);
      }
      instance.setDataset(m_OutputFormat);
      m_OutputQueue.push(instance);
    }
  }

  /**
   * Clears the output queue.
   */
  protected void resetQueue() {

    m_OutputQueue = new Queue();
  }

  /**
   * Adds the supplied input instance to the inputformat dataset for later
   * processing. Use this method rather than getInputFormat().add(instance). Or
   * else. Note that the provided instance gets copied when buffered.
   *
   * @param instance the Instance to buffer.
   */
  protected void bufferInput(Instance instance) {

    if (instance != null) {
      instance = (Instance) instance.copy(); // Don't modify incoming
      // instance!
      copyValues(instance, true);
      m_InputFormat.add(instance);
    }
  }

  /**
   * Initializes the input attribute locators. If indices is null then all
   * attributes of the data will be considered, otherwise only the ones that
   * were provided.
   *
   * @param data the data to initialize the locators with
   * @param indices if not null, the indices to which to restrict the locating
   */
  protected void initInputLocators(Instances data, int[] indices) {
    if (indices == null) {
      m_InputStringAtts = new StringLocator(data);
      m_InputRelAtts = new RelationalLocator(data);
    } else {
      m_InputStringAtts = new StringLocator(data, indices);
      m_InputRelAtts = new RelationalLocator(data, indices);
    }
  }

  /**
   * Initializes the output attribute locators. If indices is null then all
   * attributes of the data will be considered, otherwise only the ones that
   * were provided.
   *
   * @param data the data to initialize the locators with
   * @param indices if not null, the indices to which to restrict the locating
   */
  protected void initOutputLocators(Instances data, int[] indices) {
    if (indices == null) {
      m_OutputStringAtts = new StringLocator(data);
      m_OutputRelAtts = new RelationalLocator(data);
    } else {
      m_OutputStringAtts = new StringLocator(data, indices);
      m_OutputRelAtts = new RelationalLocator(data, indices);
    }
  }

  /**
   * Copies string/relational values contained in the instance copied to a new
   * dataset. The Instance must already be assigned to a dataset. This dataset
   * and the destination dataset must have the same structure.
   *
   * @param instance the Instance containing the string/relational values to
   *          copy.
   * @param isInput if true the input format and input attribute locators are
   *          used otherwise the output format and output locators
   */
  protected void copyValues(Instance instance, boolean isInput) {

    RelationalLocator.copyRelationalValues(instance, (isInput) ? m_InputFormat
      : m_OutputFormat, (isInput) ? m_InputRelAtts : m_OutputRelAtts);

    StringLocator.copyStringValues(instance, (isInput) ? m_InputFormat
      : m_OutputFormat, (isInput) ? m_InputStringAtts : m_OutputStringAtts);
  }

  /**
   * Takes string/relational values referenced by an Instance and copies them
   * from a source dataset to a destination dataset. The instance references are
   * updated to be valid for the destination dataset. The instance may have the
   * structure (i.e. number and attribute position) of either dataset (this
   * affects where references are obtained from). Only works if the number of
   * string/relational attributes is the same in both indices (implicitly these
   * string/relational attributes should be semantically same but just with
   * shifted positions).
   *
   * @param instance the instance containing references to strings/ relational
   *          values in the source dataset that will have references updated to
   *          be valid for the destination dataset.
   * @param instSrcCompat true if the instance structure is the same as the
   *          source, or false if it is the same as the destination (i.e. which
   *          of the string/relational attribute indices contains the correct
   *          locations for this instance).
   * @param srcDataset the dataset for which the current instance
   *          string/relational value references are valid (after any position
   *          mapping if needed)
   * @param destDataset the dataset for which the current instance
   *          string/relational value references need to be inserted (after any
   *          position mapping if needed)
   */
  protected void copyValues(Instance instance, boolean instSrcCompat,
    Instances srcDataset, Instances destDataset) {

    RelationalLocator.copyRelationalValues(instance, instSrcCompat, srcDataset,
      m_InputRelAtts, destDataset, m_OutputRelAtts);

    StringLocator.copyStringValues(instance, instSrcCompat, srcDataset,
      m_InputStringAtts, getOutputFormat(), m_OutputStringAtts);
  }

  /**
   * This will remove all buffered instances from the inputformat dataset. Use
   * this method rather than getInputFormat().delete();
   */
  protected void flushInput() {

    if ((m_InputStringAtts.getAttributeIndices().length > 0)
      || (m_InputRelAtts.getAttributeIndices().length > 0)) {
      m_InputFormat = m_InputFormat.stringFreeStructure();
      m_InputStringAtts = new StringLocator(m_InputFormat,
        m_InputStringAtts.getAllowedIndices());
      m_InputRelAtts = new RelationalLocator(m_InputFormat,
        m_InputRelAtts.getAllowedIndices());
    } else {
      // This more efficient than new Instances(m_InputFormat, 0);
      m_InputFormat.delete();
    }
  }

  /**
   * tests the data whether the filter can actually handle it
   *
   * @param instanceInfo the data to test
   * @throws Exception if the test fails
   */
  protected void testInputFormat(Instances instanceInfo) throws Exception {
    getCapabilities(instanceInfo).testWithFail(instanceInfo);
  }

  /**
   * Sets the format of the input instances. If the filter is able to determine
   * the output format before seeing any input instances, it does so here. This
   * default implementation clears the output format and output queue, and the
   * new batch flag is set. Overriders should call
   * super.setInputFormat(Instances)
   *
   * @param instanceInfo an Instances object containing the input instance
   *          structure (any instances contained in the object are ignored -
   *          only the structure is required).
   * @return true if the outputFormat may be collected immediately
   * @throws Exception if the inputFormat can't be set successfully
   */
  public boolean setInputFormat(Instances instanceInfo) throws Exception {

    testInputFormat(instanceInfo);

    m_InputFormat = instanceInfo.stringFreeStructure();
    m_OutputFormat = null;
    m_OutputQueue = new Queue();
    m_NewBatch = true;
    m_FirstBatchDone = false;
    initInputLocators(m_InputFormat, null);
    return false;
  }

  /**
   * Gets the format of the output instances. This should only be called after
   * input() or batchFinished() has returned true. The relation name of the
   * output instances should be changed to reflect the action of the filter (eg:
   * add the filter name and options).
   *
   * @return an Instances object containing the output instance structure only.
   * @throws NullPointerException if no input structure has been defined (or the
   *           output format hasn't been determined yet)
   */
  public Instances getOutputFormat() {

    if (m_OutputFormat == null) {
      throw new NullPointerException("No output format defined.");
    }
    return new Instances(m_OutputFormat, 0);
  }

  /**
   * Input an instance for filtering. Ordinarily the instance is processed and
   * made available for output immediately. Some filters require all instances
   * be read before producing output, in which case output instances should be
   * collected after calling batchFinished(). If the input marks the start of a
   * new batch, the output queue is cleared. This default implementation assumes
   * all instance conversion will occur when batchFinished() is called.
   *
   * @param instance the input instance
   * @return true if the filtered instance may now be collected with output().
   * @throws NullPointerException if the input format has not been defined.
   * @throws Exception if the input instance was not of the correct format or if
   *           there was a problem with the filtering.
   */
  public boolean input(Instance instance) throws Exception {

    if (m_InputFormat == null) {
      throw new NullPointerException("No input instance format defined");
    }
    if (m_NewBatch) {
      m_OutputQueue = new Queue();
      m_NewBatch = false;
    }
    bufferInput(instance);
    return false;
  }

  /**
   * Signify that this batch of input to the filter is finished. If the filter
   * requires all instances prior to filtering, output() may now be called to
   * retrieve the filtered instances. Any subsequent instances filtered should
   * be filtered based on setting obtained from the first batch (unless the
   * inputFormat has been re-assigned or new options have been set). This
   * default implementation assumes all instance processing occurs during
   * inputFormat() and input().
   *
   * @return true if there are instances pending output
   * @throws NullPointerException if no input structure has been defined,
   * @throws Exception if there was a problem finishing the batch.
   */
  public boolean batchFinished() throws Exception {

    if (m_InputFormat == null) {
      throw new NullPointerException("No input instance format defined");
    }
    flushInput();
    m_NewBatch = true;
    m_FirstBatchDone = true;

    if (m_OutputQueue.empty()) {
      // Clear out references to old strings/relationals occasionally
      if ((m_OutputStringAtts.getAttributeIndices().length > 0)
        || (m_OutputRelAtts.getAttributeIndices().length > 0)) {
        m_OutputFormat = m_OutputFormat.stringFreeStructure();
        m_OutputStringAtts = new StringLocator(m_OutputFormat,
          m_OutputStringAtts.getAllowedIndices());
      }
    }

    return (numPendingOutput() != 0);
  }

  /**
   * Output an instance after filtering and remove from the output queue.
   *
   * @return the instance that has most recently been filtered (or null if the
   *         queue is empty).
   * @throws NullPointerException if no output structure has been defined
   */
  public Instance output() {

    if (m_OutputFormat == null) {
      throw new NullPointerException("No output instance format defined");
    }
    if (m_OutputQueue.empty()) {
      return null;
    }
    Instance result = (Instance) m_OutputQueue.pop();

    // Clear out references to old strings/relationals occasionally
    /*
     * if (m_OutputQueue.empty() && m_NewBatch) { if (
     * (m_OutputStringAtts.getAttributeIndices().length > 0) ||
     * (m_OutputRelAtts.getAttributeIndices().length > 0) ) { m_OutputFormat =
     * m_OutputFormat.stringFreeStructure(); } }
     */
    return result;
  }

  /**
   * Output an instance after filtering but do not remove from the output queue.
   *
   * @return the instance that has most recently been filtered (or null if the
   *         queue is empty).
   * @throws NullPointerException if no input structure has been defined
   */
  public Instance outputPeek() {

    if (m_OutputFormat == null) {
      throw new NullPointerException("No output instance format defined");
    }
    if (m_OutputQueue.empty()) {
      return null;
    }
    Instance result = (Instance) m_OutputQueue.peek();
    return result;
  }

  /**
   * Returns the number of instances pending output
   *
   * @return the number of instances pending output
   * @throws NullPointerException if no input structure has been defined
   */
  public int numPendingOutput() {

    if (m_OutputFormat == null) {
      throw new NullPointerException("No output instance format defined");
    }
    return m_OutputQueue.size();
  }

  /**
   * Returns whether the output format is ready to be collected
   *
   * @return true if the output format is set
   */
  public boolean isOutputFormatDefined() {

    return (m_OutputFormat != null);
  }

  /**
   * Creates a deep copy of the given filter using serialization.
   *
   * @param model the filter to copy
   * @return a deep copy of the filter
   * @throws Exception if an error occurs
   */
  public static Filter makeCopy(Filter model) throws Exception {
    return (Filter) new SerializedObject(model).getObject();
  }

  /**
   * Creates a given number of deep copies of the given filter using
   * serialization.
   *
   * @param model the filter to copy
   * @param num the number of filter copies to create.
   * @return an array of filters.
   * @throws Exception if an error occurs
   */
  public static Filter[] makeCopies(Filter model, int num) throws Exception {

    if (model == null) {
      throw new Exception("No model filter set");
    }
    Filter[] filters = new Filter[num];
    SerializedObject so = new SerializedObject(model);
    for (int i = 0; i < filters.length; i++) {
      filters[i] = (Filter) so.getObject();
    }
    return filters;
  }

  /**
   * Filters an entire set of instances through a filter and returns the new
   * set.
   *
   * @param data the data to be filtered
   * @param filter the filter to be used
   * @return the filtered set of data
   * @throws Exception if the filter can't be used successfully
   */
  public static Instances useFilter(Instances data, Filter filter)
    throws Exception {
    /*
     * System.err.println(filter.getClass().getName() + " in:" +
     * data.numInstances());
     */
    for (int i = 0; i < data.numInstances(); i++) {
      filter.input(data.instance(i));
    }
    filter.batchFinished();
    Instances newData = filter.getOutputFormat();
    Instance processed;
    while ((processed = filter.output()) != null) {
      newData.add(processed);
    }

    /*
     * System.err.println(filter.getClass().getName() + " out:" +
     * newData.numInstances());
     */
    return newData;
  }

  /**
   * Returns a description of the filter, by default only the classname.
   *
   * @return a string describing the filter
   */
  @Override
  public String toString() {
    return this.getClass().getName();
  }

  /**
   * generates source code from the filter
   *
   * @param filter the filter to output as source
   * @param className the name of the generated class
   * @param input the input data the header is generated for
   * @param output the output data the header is generated for
   * @return the generated source code
   * @throws Exception if source code cannot be generated
   */
  public static String wekaStaticWrapper(Sourcable filter, String className,
    Instances input, Instances output) throws Exception {

    StringBuffer result;
    int i;
    int n;

    result = new StringBuffer();

    result.append("// Generated with Weka " + Version.VERSION + "\n");
    result.append("//\n");
    result
      .append("// This code is public domain and comes with no warranty.\n");
    result.append("//\n");
    result.append("// Timestamp: " + new Date() + "\n");
    result.append("// Relation: " + input.relationName() + "\n");
    result.append("\n");

    result.append("package weka.filters;\n");
    result.append("\n");
    result.append("import weka.core.Attribute;\n");
    result.append("import weka.core.Capabilities;\n");
    result.append("import weka.core.Capabilities.Capability;\n");
    result.append("import weka.core.DenseInstance;\n");
    result.append("import weka.core.Instance;\n");
    result.append("import weka.core.Instances;\n");
    result.append("import weka.core.Utils;\n");
    result.append("import weka.filters.Filter;\n");
    result.append("import java.util.ArrayList;\n");
    result.append("\n");
    result.append("public class WekaWrapper\n");
    result.append("  extends Filter {\n");

    // globalInfo
    result.append("\n");
    result.append("  /**\n");
    result.append("   * Returns only the toString() method.\n");
    result.append("   *\n");
    result.append("   * @return a string describing the filter\n");
    result.append("   */\n");
    result.append("  public String globalInfo() {\n");
    result.append("    return toString();\n");
    result.append("  }\n");

    // getCapabilities
    result.append("\n");
    result.append("  /**\n");
    result.append("   * Returns the capabilities of this filter.\n");
    result.append("   *\n");
    result.append("   * @return the capabilities\n");
    result.append("   */\n");
    result.append("  public Capabilities getCapabilities() {\n");
    result.append(((Filter) filter).getCapabilities().toSource("result", 4));
    result.append("    return result;\n");
    result.append("  }\n");

    // objectsToInstance
    result.append("\n");
    result.append("  /**\n");
    result.append("   * turns array of Objects into an Instance object\n");
    result.append("   *\n");
    result
      .append("   * @param obj	the Object array to turn into an Instance\n");
    result.append("   * @param format	the data format to use\n");
    result.append("   * @return		the generated Instance object\n");
    result.append("   */\n");
    result
      .append("  protected Instance objectsToInstance(Object[] obj, Instances format) {\n");
    result.append("    Instance		result;\n");
    result.append("    double[]		values;\n");
    result.append("    int		i;\n");
    result.append("\n");
    result.append("    values = new double[obj.length];\n");
    result.append("\n");
    result.append("    for (i = 0 ; i < obj.length; i++) {\n");
    result.append("      if (obj[i] == null)\n");
    result.append("        values[i] = Utils.missingValue();\n");
    result.append("      else if (format.attribute(i).isNumeric())\n");
    result.append("        values[i] = (Double) obj[i];\n");
    result.append("      else if (format.attribute(i).isNominal())\n");
    result
      .append("        values[i] = format.attribute(i).indexOfValue((String) obj[i]);\n");
    result.append("    }\n");
    result.append("\n");
    result.append("    // create new instance\n");
    result.append("    result = new DenseInstance(1.0, values);\n");
    result.append("    result.setDataset(format);\n");
    result.append("\n");
    result.append("    return result;\n");
    result.append("  }\n");

    // instanceToObjects
    result.append("\n");
    result.append("  /**\n");
    result.append("   * turns the Instance object into an array of Objects\n");
    result.append("   *\n");
    result.append("   * @param inst	the instance to turn into an array\n");
    result.append("   * @return		the Object array representing the instance\n");
    result.append("   */\n");
    result.append("  protected Object[] instanceToObjects(Instance inst) {\n");
    result.append("    Object[]	result;\n");
    result.append("    int		i;\n");
    result.append("\n");
    result.append("    result = new Object[inst.numAttributes()];\n");
    result.append("\n");
    result.append("    for (i = 0 ; i < inst.numAttributes(); i++) {\n");
    result.append("      if (inst.isMissing(i))\n");
    result.append("  	result[i] = null;\n");
    result.append("      else if (inst.attribute(i).isNumeric())\n");
    result.append("  	result[i] = inst.value(i);\n");
    result.append("      else\n");
    result.append("  	result[i] = inst.stringValue(i);\n");
    result.append("    }\n");
    result.append("\n");
    result.append("    return result;\n");
    result.append("  }\n");

    // instancesToObjects
    result.append("\n");
    result.append("  /**\n");
    result.append("   * turns the Instances object into an array of Objects\n");
    result.append("   *\n");
    result.append("   * @param data	the instances to turn into an array\n");
    result
      .append("   * @return		the Object array representing the instances\n");
    result.append("   */\n");
    result
      .append("  protected Object[][] instancesToObjects(Instances data) {\n");
    result.append("    Object[][]	result;\n");
    result.append("    int		i;\n");
    result.append("\n");
    result.append("    result = new Object[data.numInstances()][];\n");
    result.append("\n");
    result.append("    for (i = 0; i < data.numInstances(); i++)\n");
    result.append("      result[i] = instanceToObjects(data.instance(i));\n");
    result.append("\n");
    result.append("    return result;\n");
    result.append("  }\n");

    // setInputFormat
    result.append("\n");
    result.append("  /**\n");
    result.append("   * Only tests the input data.\n");
    result.append("   *\n");
    result
      .append("   * @param instanceInfo the format of the data to convert\n");
    result
      .append("   * @return always true, to indicate that the output format can \n");
    result.append("   *         be collected immediately.\n");
    result.append("   */\n");
    result
      .append("  public boolean setInputFormat(Instances instanceInfo) throws Exception {\n");
    result.append("    super.setInputFormat(instanceInfo);\n");
    result.append("    \n");
    result.append("    // generate output format\n");
    result
      .append("    ArrayList<Attribute> atts = new ArrayList<Attribute>();\n");
    result.append("    ArrayList<String> attValues;\n");
    for (i = 0; i < output.numAttributes(); i++) {
      result.append("    // " + output.attribute(i).name() + "\n");
      if (output.attribute(i).isNumeric()) {
        result.append("    atts.add(new Attribute(\""
          + output.attribute(i).name() + "\"));\n");
      } else if (output.attribute(i).isNominal()) {
        result.append("    attValues = new ArrayList<String>();\n");
        for (n = 0; n < output.attribute(i).numValues(); n++) {
          result.append("    attValues.add(\"" + output.attribute(i).value(n)
            + "\");\n");
        }
        result.append("    atts.add(new Attribute(\""
          + output.attribute(i).name() + "\", attValues));\n");
      } else {
        throw new UnsupportedAttributeTypeException("Attribute type '"
          + output.attribute(i).type() + "' (position " + (i + 1)
          + ") is not supported!");
      }
    }
    result.append("    \n");
    result.append("    Instances format = new Instances(\""
      + output.relationName() + "\", atts, 0);\n");
    result.append("    format.setClassIndex(" + output.classIndex() + ");\n");
    result.append("    setOutputFormat(format);\n");
    result.append("    \n");
    result.append("    return true;\n");
    result.append("  }\n");

    // input
    result.append("\n");
    result.append("  /**\n");
    result.append("   * Directly filters the instance.\n");
    result.append("   *\n");
    result.append("   * @param instance the instance to convert\n");
    result
      .append("   * @return always true, to indicate that the output can \n");
    result.append("   *         be collected immediately.\n");
    result.append("   */\n");
    result
      .append("  public boolean input(Instance instance) throws Exception {\n");
    result.append("    Object[] filtered = " + className
      + ".filter(instanceToObjects(instance));\n");
    result
      .append("    push(objectsToInstance(filtered, getOutputFormat()));\n");
    result.append("    return true;\n");
    result.append("  }\n");

    // batchFinished
    result.append("\n");
    result.append("  /**\n");
    result
      .append("   * Performs a batch filtering of the buffered data, if any available.\n");
    result.append("   *\n");
    result
      .append("   * @return true if instances were filtered otherwise false\n");
    result.append("   */\n");
    result.append("  public boolean batchFinished() throws Exception {\n");
    result.append("    if (getInputFormat() == null)\n");
    result
      .append("      throw new NullPointerException(\"No input instance format defined\");;\n");
    result.append("\n");
    result.append("    Instances inst = getInputFormat();\n");
    result.append("    if (inst.numInstances() > 0) {\n");
    result.append("      Object[][] filtered = " + className
      + ".filter(instancesToObjects(inst));\n");
    result.append("      for (int i = 0; i < filtered.length; i++) {\n");
    result
      .append("        push(objectsToInstance(filtered[i], getOutputFormat()));\n");
    result.append("      }\n");
    result.append("    }\n");
    result.append("\n");
    result.append("    flushInput();\n");
    result.append("    m_NewBatch = true;\n");
    result.append("    m_FirstBatchDone = true;\n");
    result.append("\n");
    result.append("    return (inst.numInstances() > 0);\n");
    result.append("  }\n");

    // toString
    result.append("\n");
    result.append("  /**\n");
    result
      .append("   * Returns only the classnames and what filter it is based on.\n");
    result.append("   *\n");
    result.append("   * @return a short description\n");
    result.append("   */\n");
    result.append("  public String toString() {\n");
    result.append("    return \"Auto-generated filter wrapper, based on "
      + filter.getClass().getName() + " (generated with Weka "
      + Version.VERSION + ").\\n" + "\" + this.getClass().getName() + \"/"
      + className + "\";\n");
    result.append("  }\n");

    // main
    result.append("\n");
    result.append("  /**\n");
    result.append("   * Runs the filter from commandline.\n");
    result.append("   *\n");
    result.append("   * @param args the commandline arguments\n");
    result.append("   */\n");
    result.append("  public static void main(String args[]) {\n");
    result.append("    runFilter(new WekaWrapper(), args);\n");
    result.append("  }\n");
    result.append("}\n");

    // actual filter code
    result.append("\n");
    result.append(filter.toSource(className, input));

    return result.toString();
  }

  /**
   * Method for testing filters.
   *
   * @param filter the filter to use
   * @param options should contain the following arguments:

   *          -i input_file

   *          -o output_file

   *          -c class_index

   *          -z classname (for filters implementing weka.filters.Sourcable)

   *          or -h for help on options
   * @throws Exception if something goes wrong or the user requests help on
   *           command options
   */
  public static void filterFile(Filter filter, String[] options)
    throws Exception {

    boolean debug = false;
    Instances data = null;
    DataSource input = null;
    PrintWriter output = null;
    boolean helpRequest;
    String sourceCode = "";

    try {
      helpRequest = Utils.getFlag('h', options);

      if (Utils.getFlag('d', options)) {
        debug = true;
      }
      String infileName = Utils.getOption('i', options);
      String outfileName = Utils.getOption('o', options);
      String classIndex = Utils.getOption('c', options);
      if (filter instanceof Sourcable) {
        sourceCode = Utils.getOption('z', options);
      }

      if (filter instanceof OptionHandler) {
        ((OptionHandler) filter).setOptions(options);
      }

      Utils.checkForRemainingOptions(options);
      if (helpRequest) {
        throw new Exception("Help requested.\n");
      }
      if (infileName.length() != 0) {
        input = new DataSource(infileName);
      } else {
        input = new DataSource(System.in);
      }
      if (outfileName.length() != 0) {
        output = new PrintWriter(new FileOutputStream(outfileName));
      } else {
        output = new PrintWriter(System.out);
      }

      data = input.getStructure();
      if (classIndex.length() != 0) {
        if (classIndex.equals("first")) {
          data.setClassIndex(0);
        } else if (classIndex.equals("last")) {
          data.setClassIndex(data.numAttributes() - 1);
        } else {
          data.setClassIndex(Integer.parseInt(classIndex) - 1);
        }
      }
    } catch (Exception ex) {
      String filterOptions = "";
      // Output the error and also the valid options
      if (filter instanceof OptionHandler) {
        filterOptions += "\nFilter options:\n\n";
        Enumeration<Option> enu = ((OptionHandler) filter).listOptions();
        while (enu.hasMoreElements()) {
          Option option = enu.nextElement();
          filterOptions += option.synopsis() + '\n' + option.description()
            + "\n";
        }
      }

      String genericOptions = "\nGeneral options:\n\n" + "-h\n"
        + "\tGet help on available options.\n"
        + "\t(use -b -h for help on batch mode.)\n" + "-i <file>\n"
        + "\tThe name of the file containing input instances.\n"
        + "\tIf not supplied then instances will be read from stdin.\n"
        + "-o <file>\n"
        + "\tThe name of the file output instances will be written to.\n"
        + "\tIf not supplied then instances will be written to stdout.\n"
        + "-c <class index>\n"
        + "\tThe number of the attribute to use as the class.\n"
        + "\t\"first\" and \"last\" are also valid entries.\n"
        + "\tIf not supplied then no class is assigned.\n";

      if (filter instanceof Sourcable) {
        genericOptions += "-z <class name>\n"
          + "\tOutputs the source code representing the trained filter.\n";
      }

      throw new Exception('\n' + ex.getMessage() + filterOptions
        + genericOptions);
    }

    if (debug) {
      System.err.println("Setting input format");
    }
    boolean printedHeader = false;
    if (filter.setInputFormat(data)) {
      if (debug) {
        System.err.println("Getting output format");
      }
      output.println(filter.getOutputFormat().toString());
      printedHeader = true;
    }

    // Pass all the instances to the filter
    Instance inst;
    while (input.hasMoreElements(data)) {
      inst = input.nextElement(data);
      if (debug) {
        System.err.println("Input instance to filter");
      }
      if (filter.input(inst)) {
        if (debug) {
          System.err.println("Filter said collect immediately");
        }
        if (!printedHeader) {
          throw new Error("Filter didn't return true from setInputFormat() "
            + "earlier!");
        }
        if (debug) {
          System.err.println("Getting output instance");
        }
        output.println(filter.output().toString());
      }
    }

    // Say that input has finished, and print any pending output instances
    if (debug) {
      System.err.println("Setting end of batch");
    }
    if (filter.batchFinished()) {
      if (debug) {
        System.err.println("Filter said collect output");
      }
      if (!printedHeader) {
        if (debug) {
          System.err.println("Getting output format");
        }
        output.println(filter.getOutputFormat().toString());
      }
      if (debug) {
        System.err.println("Getting output instance");
      }
      while (filter.numPendingOutput() > 0) {
        output.println(filter.output().toString());
        if (debug) {
          System.err.println("Getting output instance");
        }
      }
    }
    if (debug) {
      System.err.println("Done");
    }

    if (output != null) {
      output.close();
    }

    if (sourceCode.length() != 0) {
      System.out.println(wekaStaticWrapper((Sourcable) filter, sourceCode,
        data, filter.getOutputFormat()));
    }
  }

  /**
   * Method for testing filters ability to process multiple batches.
   *
   * @param filter the filter to use
   * @param options should contain the following arguments:

   *          -i (first) input file

   *          -o (first) output file

   *          -r (second) input file

   *          -s (second) output file

   *          -c class_index

   *          -z classname (for filters implementing weka.filters.Sourcable)

   *          or -h for help on options
   * @throws Exception if something goes wrong or the user requests help on
   *           command options
   */
  public static void batchFilterFile(Filter filter, String[] options)
    throws Exception {

    Instances firstData = null;
    Instances secondData = null;
    DataSource firstInput = null;
    DataSource secondInput = null;
    PrintWriter firstOutput = null;
    PrintWriter secondOutput = null;
    boolean helpRequest;
    String sourceCode = "";

    try {
      helpRequest = Utils.getFlag('h', options);

      String fileName = Utils.getOption('i', options);
      if (fileName.length() != 0) {
        firstInput = new DataSource(fileName);
      } else {
        throw new Exception("No first input file given.\n");
      }

      fileName = Utils.getOption('r', options);
      if (fileName.length() != 0) {
        secondInput = new DataSource(fileName);
      } else {
        throw new Exception("No second input file given.\n");
      }

      fileName = Utils.getOption('o', options);
      if (fileName.length() != 0) {
        firstOutput = new PrintWriter(new FileOutputStream(fileName));
      } else {
        firstOutput = new PrintWriter(System.out);
      }

      fileName = Utils.getOption('s', options);
      if (fileName.length() != 0) {
        secondOutput = new PrintWriter(new FileOutputStream(fileName));
      } else {
        secondOutput = new PrintWriter(System.out);
      }
      String classIndex = Utils.getOption('c', options);
      if (filter instanceof Sourcable) {
        sourceCode = Utils.getOption('z', options);
      }

      if (filter instanceof OptionHandler) {
        ((OptionHandler) filter).setOptions(options);
      }
      Utils.checkForRemainingOptions(options);

      if (helpRequest) {
        throw new Exception("Help requested.\n");
      }
      firstData = firstInput.getStructure();
      secondData = secondInput.getStructure();
      if (!secondData.equalHeaders(firstData)) {
        throw new Exception("Input file formats differ.\n"
          + secondData.equalHeadersMsg(firstData) + "\n");
      }
      if (classIndex.length() != 0) {
        if (classIndex.equals("first")) {
          firstData.setClassIndex(0);
          secondData.setClassIndex(0);
        } else if (classIndex.equals("last")) {
          firstData.setClassIndex(firstData.numAttributes() - 1);
          secondData.setClassIndex(secondData.numAttributes() - 1);
        } else {
          firstData.setClassIndex(Integer.parseInt(classIndex) - 1);
          secondData.setClassIndex(Integer.parseInt(classIndex) - 1);
        }
      }
    } catch (Exception ex) {
      String filterOptions = "";
      // Output the error and also the valid options
      if (filter instanceof OptionHandler) {
        filterOptions += "\nFilter options:\n\n";
        Enumeration<Option> enu = ((OptionHandler) filter).listOptions();
        while (enu.hasMoreElements()) {
          Option option = enu.nextElement();
          filterOptions += option.synopsis() + '\n' + option.description()
            + "\n";
        }
      }

      String genericOptions = "\nGeneral options:\n\n" + "-h\n"
        + "\tGet help on available options.\n" + "-i <filename>\n"
        + "\tThe file containing first input instances.\n" + "-o <filename>\n"
        + "\tThe file first output instances will be written to.\n"
        + "-r <filename>\n" + "\tThe file containing second input instances.\n"
        + "-s <filename>\n"
        + "\tThe file second output instances will be written to.\n"
        + "-c <class index>\n"
        + "\tThe number of the attribute to use as the class.\n"
        + "\t\"first\" and \"last\" are also valid entries.\n"
        + "\tIf not supplied then no class is assigned.\n";

      if (filter instanceof Sourcable) {
        genericOptions += "-z <class name>\n"
          + "\tOutputs the source code representing the trained filter.\n";
      }

      throw new Exception('\n' + ex.getMessage() + filterOptions
        + genericOptions);
    }
    boolean printedHeader = false;
    if (filter.setInputFormat(firstData)) {
      firstOutput.println(filter.getOutputFormat().toString());
      printedHeader = true;
    }

    // Pass all the instances to the filter
    Instance inst;
    while (firstInput.hasMoreElements(firstData)) {
      inst = firstInput.nextElement(firstData);
      if (filter.input(inst)) {
        if (!printedHeader) {
          throw new Error("Filter didn't return true from setInputFormat() "
            + "earlier!");
        }
        firstOutput.println(filter.output().toString());
      }
    }

    // Say that input has finished, and print any pending output instances
    if (filter.batchFinished()) {
      if (!printedHeader) {
        firstOutput.println(filter.getOutputFormat().toString());
      }
      while (filter.numPendingOutput() > 0) {
        firstOutput.println(filter.output().toString());
      }
    }

    if (firstOutput != null) {
      firstOutput.close();
    }
    printedHeader = false;
    if (filter.isOutputFormatDefined()) {
      secondOutput.println(filter.getOutputFormat().toString());
      printedHeader = true;
    }
    // Pass all the second instances to the filter
    while (secondInput.hasMoreElements(secondData)) {
      inst = secondInput.nextElement(secondData);
      if (filter.input(inst)) {
        if (!printedHeader) {
          throw new Error("Filter didn't return true from"
            + " isOutputFormatDefined() earlier!");
        }
        secondOutput.println(filter.output().toString());
      }
    }

    // Say that input has finished, and print any pending output instances
    if (filter.batchFinished()) {
      if (!printedHeader) {
        secondOutput.println(filter.getOutputFormat().toString());
      }
      while (filter.numPendingOutput() > 0) {
        secondOutput.println(filter.output().toString());
      }
    }
    if (secondOutput != null) {
      secondOutput.close();
    }

    if (sourceCode.length() != 0) {
      System.out.println(wekaStaticWrapper((Sourcable) filter, sourceCode,
        firstData, filter.getOutputFormat()));
    }
  }

  /**
   * runs the filter instance with the given options.
   *
   * @param filter the filter to run
   * @param options the commandline options
   */
  public static void runFilter(Filter filter, String[] options) {
    try {
      if (Utils.getFlag('b', options)) {
        Filter.batchFilterFile(filter, options);
      } else {
        Filter.filterFile(filter, options);
      }
    } catch (Exception e) {
      if ((e.toString().indexOf("Help requested") == -1)
        && (e.toString().indexOf("Filter options") == -1)) {
        e.printStackTrace();
      } else {
        System.err.println(e.getMessage());
      }
    }
  }

  /**
   * Returns an enumeration describing the available options.
   *
   * @return an enumeration of all the available options.
   */
  @Override
  public Enumeration<Option> listOptions() {

    Vector<Option> newVector = new Vector<Option>(2);

    newVector.addElement(new Option(
      "\tIf set, filter is run in debug mode and\n"
        + "\tmay output additional info to the console", "output-debug-info",
      0, "-output-debug-info"));
    newVector
      .addElement(new Option(
        "\tIf set, filter capabilities are not checked before filter is built\n"
          + "\t(use with caution).", "-do-not-check-capabilities", 0,
        "-do-not-check-capabilities"));

    return newVector.elements();
  }

  /**
   * Parses a given list of options. Valid options are:
   *

   *
   * -D

   * If set, filter is run in debug mode and may output additional info to
   * the console.
   *

   *
   * -do-not-check-capabilities

   * If set, filter capabilities are not checked before filter is built
   * (use with caution).
   *

   *
   * @param options the list of options as an array of strings
   * @exception Exception if an option is not supported
   */
  @Override
  public void setOptions(String[] options) throws Exception {

    setDebug(Utils.getFlag("output-debug-info", options));
    setDoNotCheckCapabilities(Utils.getFlag("do-not-check-capabilities",
      options));
  }

  /**
   * Gets the current settings of the filter.
   *
   * @return an array of strings suitable for passing to setOptions
   */
  @Override
  public String[] getOptions() {

    Vector<String> options = new Vector<String>();

    if (getDebug()) {
      options.add("-output-debug-info");
    }
    if (getDoNotCheckCapabilities()) {
      options.add("-do-not-check-capabilities");
    }

    return options.toArray(new String[0]);
  }

  /**
   * Set debugging mode.
   *
   * @param debug true if debug output should be printed
   */
  public void setDebug(boolean debug) {

    m_Debug = debug;
  }

  /**
   * Get whether debugging is turned on.
   *
   * @return true if debugging output is on
   */
  public boolean getDebug() {

    return m_Debug;
  }

  /**
   * Returns the tip text for this property
   *
   * @return tip text for this property suitable for displaying in the
   *         explorer/experimenter gui
   */
  public String debugTipText() {
    return "If set to true, filter may output additional info to "
      + "the console.";
  }

  /**
   * Set whether not to check capabilities.
   *
   * @param doNotCheckCapabilities true if capabilities are not to be checked.
   */
  public void setDoNotCheckCapabilities(boolean doNotCheckCapabilities) {

    m_DoNotCheckCapabilities = doNotCheckCapabilities;
  }

  /**
   * Get whether capabilities checking is turned off.
   *
   * @return true if capabilities checking is turned off.
   */
  public boolean getDoNotCheckCapabilities() {

    return m_DoNotCheckCapabilities;
  }

  /**
   * Returns the tip text for this property
   *
   * @return tip text for this property suitable for displaying in the
   *         explorer/experimenter gui
   */
  public String doNotCheckCapabilitiesTipText() {
    return "If set, filters capabilities are not checked before filter is built"
      + " (Use with caution to reduce runtime).";
  }

  /**
   * Main method for testing this class.
   *
   * @param args should contain arguments to the filter: use -h for help
   */
  public static void main(String[] args) {

    try {
      if (args.length == 0) {
        throw new Exception("First argument must be the class name of a Filter");
      }
      String fname = args[0];
      Filter f = (Filter) Class.forName(fname).newInstance();
      args[0] = "";
      runFilter(f, args);
    } catch (Exception ex) {
      ex.printStackTrace();
      System.err.println(ex.getMessage());
    }
  }
}


text:
package com.mxgraph.analysis;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.mxgraph.costfunction.mxCostFunction;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGraphModel;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.view.mxCellState;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxGraph.mxICellVisitor;
import com.mxgraph.view.mxGraphView;

public class mxGraphStructure
{
	/**
	 * The default style for vertexes
	 */
	private static String basicVertexStyleString = "ellipse;strokeColor=black;fillColor=orange;gradientColor=none";

	/**
	 * The default style for edges
	 */
	private static String basicEdgeStyleString = "strokeColor=red;noEdgeStyle=1;";

	private static String basicArrowStyleString = "endArrow=block;";

	/**
	 * @param aGraph
	 * @return true if the graph is connected
	 */
	public static boolean isConnected(mxAnalysisGraph aGraph)
	{
		Object[] vertices = aGraph.getChildVertices(aGraph.getGraph().getDefaultParent());
		int vertexNum = vertices.length;

		if (vertexNum == 0)
		{
			throw new IllegalArgumentException();
		}

		//data preparation
		int connectedVertices = 1;
		int[] visited = new int[vertexNum];
		visited[0] = 1;

		for (int i = 1; i < vertexNum; i++)
		{
			visited[i] = 0;
		}

		ArrayList<Object> queue = new ArrayList<Object>();
		queue.add(vertices[0]);

		//repeat the algorithm until the queue is empty
		while (queue.size() > 0)
		{
			//cut out the first vertex
			Object currVertex = queue.get(0);
			queue.remove(0);

			//fill the queue with neighboring but unvisited vertexes
			Object[] neighborVertices = aGraph.getOpposites(aGraph.getEdges(currVertex, null, true, true, false, true), currVertex, true,
					true);

			for (int j = 0; j < neighborVertices.length; j++)
			{
				//get the index of the neighbor vertex
				int index = 0;

				for (int k = 0; k < vertexNum; k++)
				{
					if (vertices[k].equals(neighborVertices[j]))
					{
						index = k;
					}
				}

				if (visited[index] == 0)
				{
					queue.add(vertices[index]);
					visited[index] = 1;
					connectedVertices++;
				}
			}
		}

		// if we visited every vertex, the graph is connected
		if (connectedVertices == vertexNum)
		{
			return true;
		}
		else
		{
			return false;
		}
	};

	/**
	 * @param aGraph
	 * @param parent
	 * @return true if the graph contains cycles regardless of edge direction
	 */
	public static boolean isCyclicUndirected(mxAnalysisGraph aGraph)
	{
		mxGraph graph = aGraph.getGraph();
		mxIGraphModel model = graph.getModel();
		Object[] cells = model.cloneCells(aGraph.getChildCells(graph.getDefaultParent(), true, true), true);
		mxGraphModel modelCopy = new mxGraphModel();
		mxGraph graphCopy = new mxGraph(modelCopy);
		Object parentCopy = graphCopy.getDefaultParent();
		graphCopy.addCells(cells);
		//		mxAnalysisGraph aGraphCopy = new mxAnalysisGraph(graphCopy, aGraph.getGenerator(), aGraph.getProperties());
		mxAnalysisGraph aGraphCopy = new mxAnalysisGraph();
		aGraphCopy.setGraph(graphCopy);
		aGraphCopy.setGenerator(aGraph.getGenerator());
		aGraphCopy.setProperties(aGraph.getProperties());

		Object[] leaf = new Object[1];

		do
		{
			leaf[0] = getUndirectedLeaf(aGraphCopy);

			if (leaf[0] != null)
			{
				graphCopy.removeCells(leaf);
			}
		}
		while (leaf[0] != null);

		int vertexNum = aGraphCopy.getChildVertices(parentCopy).length;

		if (vertexNum > 0)
		{
			return true;
		}
		else
		{
			return false;
		}

	};

	/**
	 * A helper function for getting a leaf vertex (degree <= 1), not taking into account edge direction - for internal use
	 * @param aGraph
	 * @return the first undirected leaf that could be found in the graph, null if none
	 */
	private static Object getUndirectedLeaf(mxAnalysisGraph aGraph)
	{
		Object parent = aGraph.getGraph().getDefaultParent();
		Object[] vertices = aGraph.getChildVertices(parent);
		int vertexNum = vertices.length;
		Object currVertex;

		for (int i = 0; i < vertexNum; i++)
		{
			currVertex = vertices[i];
			int edgeCount = aGraph.getEdges(currVertex, parent, true, true, false, true).length;

			if (edgeCount <= 1)
			{
				return currVertex;
			}
		}

		return null;
	};

	/**
	 * @param aGraph
	 * @return true if the graph is simple (no self loops and no multiple edges)
	 */
	public static boolean isSimple(mxAnalysisGraph aGraph)
	{
		Object parent = aGraph.getGraph().getDefaultParent();
		Object[] edges = aGraph.getChildEdges(parent);

		// self loop detection
		for (int i = 0; i < edges.length; i++)
		{
			Object currEdge = edges[i];

			if (aGraph.getTerminal(currEdge, true) == aGraph.getTerminal(currEdge, false))
			{
				return false;
			}

			for (int j = 0; j < edges.length; j++)
			{
				Object currEdge2 = edges[j];

				if (currEdge != currEdge2)
				{
					if (aGraph.getTerminal(currEdge, true) == aGraph.getTerminal(currEdge2, true)
							&& aGraph.getTerminal(currEdge, false) == aGraph.getTerminal(currEdge2, false))
					{
						return false;
					}

					if (aGraph.getTerminal(currEdge, true) == aGraph.getTerminal(currEdge2, false)
							&& aGraph.getTerminal(currEdge, false) == aGraph.getTerminal(currEdge2, true))
					{
						return false;
					}
				}
			}
		}

		return true;
	};

	/**
	 * @param aGraph
	 * @return true if the graph has the structure of a tree, regardless of edge direction
	 */
	public static boolean isTree(mxAnalysisGraph aGraph)
	{
		if (isConnected(aGraph) && !isCyclicUndirected(aGraph) && isSimple(aGraph))
		{
			return true;
		}

		return false;
	};

	/**
	 * @param aGraph
	 * @param omitVertex vertices in this array will be omitted, set this parameter to null if you don't want this feature
	 * @return a vertex that has lowest degree, or one of those in case if there are more
	 */
	static public Object getLowestDegreeVertex(mxAnalysisGraph aGraph, Object[] omitVertex)
	{
		Object[] vertices = aGraph.getChildVertices(aGraph.getGraph().getDefaultParent());
		int vertexCount = vertices.length;

		int lowestEdgeCount = Integer.MAX_VALUE;
		Object bestVertex = null;
		List<Object> omitList = null;

		if (omitVertex != null)
		{
			omitList = Arrays.asList(omitVertex);
		}

		for (int i = 0; i < vertexCount; i++)
		{
			if (omitVertex == null || !omitList.contains(vertices[i]))
			{
				int currEdgeCount = aGraph.getEdges(vertices[i], null, true, true, true, true).length;

				if (currEdgeCount == 0)
				{
					return vertices[i];
				}
				else
				{
					if (currEdgeCount < lowestEdgeCount)
					{
						lowestEdgeCount = currEdgeCount;
						bestVertex = vertices[i];
					}
				}
			}
		}

		return bestVertex;
	};

	/**
	 * @param graph
	 * @param sourceVertex
	 * @param targetVertex
	 * @return Returns true if the two vertices are connected directly by an edge. If directed, the result is true if they are connected by an edge that points from source to target, if false direction isn't takein into account, just connectivity.
	 */
	public static boolean areConnected(mxAnalysisGraph aGraph, Object sourceVertex, Object targetVertex)
	{
		Object currEdges[] = aGraph.getEdges(sourceVertex, aGraph.getGraph().getDefaultParent(), true, true, false, true);
		List<Object> neighborList = Arrays.asList(aGraph.getOpposites(currEdges, sourceVertex, true, true));
		return neighborList.contains(targetVertex);
	};

	/**
	 * @param graph
	 * Make a graph simple (remove parallel edges and self loops)
	 */
	public static void makeSimple(mxAnalysisGraph aGraph)
	{
		// remove all self-loops
		// reduce all valences >1 to 1
		mxGraph graph = aGraph.getGraph();
		Object parent = graph.getDefaultParent();

		Object[] edges = aGraph.getChildEdges(parent);
		//removing self-loops
		for (int i = 0; i < edges.length; i++)
		{
			Object currEdge = edges[i];

			if (aGraph.getTerminal(currEdge, true) == aGraph.getTerminal(currEdge, false))
			{
				graph.removeCells(new Object[] { currEdge });
			}
		}

		edges = graph.getChildEdges(parent);
		Set<Set<Object>> vertexSet = new HashSet<Set<Object>>();
		ArrayList<Object> duplicateEdges = new ArrayList<Object>();

		for (int i = 0; i < edges.length; i++)
		{
			Object currEdge = edges[i];
			Object source = aGraph.getTerminal(currEdge, true);
			Object target = aGraph.getTerminal(currEdge, false);
			Set<Object> currSet = new HashSet<Object>();
			currSet.add(source);
			currSet.add(target);

			if (vertexSet.contains(currSet))
			{
				//we have a duplicate edge
				duplicateEdges.add(currEdge);
			}
			else
			{
				vertexSet.add(currSet);
			}
		}

		Object[] duplEdges = duplicateEdges.toArray();

		graph.removeCells(duplEdges);
	};

	/**
	 * Makes the graph connected
	 * @param aGraph
	 */
	public static void makeConnected(mxAnalysisGraph aGraph)
	{
		// an early check, to avoid running getGraphComponents() needlessly, which is CPU intensive
		if (mxGraphStructure.isConnected(aGraph))
		{
			return;
		}

		Object[][] components = getGraphComponents(aGraph);
		int componentNum = components.length;

		if (componentNum < 2)
		{
			return;
		}

		mxGraph graph = aGraph.getGraph();
		Object parent = graph.getDefaultParent();

		// find a random vertex in each group and connect them.
		for (int i = 1; i < componentNum; i++)
		{
			Object sourceVertex = components[i - 1][(int) Math.round(Math.random() * (components[i - 1].length - 1))];
			Object targetVertex = components[i][(int) Math.round(Math.random() * (components[i].length - 1))];
			graph.insertEdge(parent, null, aGraph.getGenerator().getNewEdgeValue(aGraph), sourceVertex, targetVertex);
		}
	};

	/**
	 * @param aGraph
	 * @return Object[components][vertices]
	 */
	public static Object[][] getGraphComponents(mxAnalysisGraph aGraph)
	{
		Object parent = aGraph.getGraph().getDefaultParent();
		Object[] vertices = aGraph.getChildVertices(parent);
		int vertexCount = vertices.length;

		if (vertexCount == 0)
		{
			return null;
		}

		ArrayList<ArrayList<Object>> componentList = new ArrayList<ArrayList<Object>>();
		ArrayList<Object> unvisitedVertexList = new ArrayList<Object>(Arrays.asList(vertices));
		boolean oldDirectedness = mxGraphProperties.isDirected(aGraph.getProperties(), mxGraphProperties.DEFAULT_DIRECTED);
		mxGraphProperties.setDirected(aGraph.getProperties(), false);

		while (unvisitedVertexList.size() > 0)
		{
			//check if the current vertex isn't already in a component

			//if yes, just remove it from the unvisited list
			Object currVertex = unvisitedVertexList.remove(0);
			int componentCount = componentList.size();
			boolean isInComponent = false;

			for (int i = 0; i < componentCount; i++)
			{
				if (componentList.get(i).contains(currVertex))
				{
					isInComponent = true;
				}
			}

			//if not, create a new component and run a BFS populating the component and reducing the unvisited list
			if (!isInComponent)
			{
				final ArrayList<Object> currVertexList = new ArrayList<Object>();

				mxTraversal.bfs(aGraph, currVertex, new mxICellVisitor()
				{
					public boolean visit(Object vertex, Object edge)
					{
						currVertexList.add(vertex);
						return false;
					}
				});

				for (int i = 0; i < currVertexList.size(); i++)
				{
					unvisitedVertexList.remove(currVertexList.get(i));
				}

				componentList.add(currVertexList);
			}
		}

		mxGraphProperties.setDirected(aGraph.getProperties(), oldDirectedness);
		Object[][] result = new Object[componentList.size()][];

		for (int i = 0; i < componentList.size(); i++)
		{
			result[i] = componentList.get(i).toArray();
		}

		return (Object[][]) result;
	};

	/**
	 * Makes a tree graph directed from the source to the leaves
	 * @param aGraph
	 * @param startVertex - this vertex will be root of the tree (the only source node)
	 * @throws StructuralException - the graph must be a tree (edge direction doesn't matter)
	 */
	public static void makeTreeDirected(mxAnalysisGraph aGraph, Object startVertex) throws StructuralException
	{
		if (isTree(aGraph))
		{
			mxGraphProperties.setDirected(aGraph.getProperties(), false);
			final ArrayList<Object> bFSList = new ArrayList<Object>();
			mxGraph graph = aGraph.getGraph();
			final mxIGraphModel model = graph.getModel();
			Object parent = graph.getDefaultParent();

			mxTraversal.bfs(aGraph, startVertex, new mxICellVisitor()
			{
				public boolean visit(Object vertex, Object edge)
				{
					bFSList.add(vertex);
					return false;
				}
			});

			for (int i = 0; i < bFSList.size(); i++)
			{
				Object parentVertex = bFSList.get(i);
				Object currEdges[] = aGraph.getEdges(parentVertex, parent, true, true, false, true);
				Object[] neighbors = aGraph.getOpposites(currEdges, parentVertex, true, true);

				for (int j = 0; j < neighbors.length; j++)
				{
					Object currVertex = neighbors[j];
					int childIndex = bFSList.indexOf(currVertex);

					if (childIndex > i)
					{
						//parentVertex is parent of currVertex, so the edge must be directed from parentVertex to currVertex
						// but we need to find the connecting edge first
						Object currEdge = getConnectingEdge(aGraph, parentVertex, currVertex);
						model.setTerminal(currEdge, parentVertex, true);
						model.setTerminal(currEdge, currVertex, false);
					}
				}
			}

			mxGraphProperties.setDirected(aGraph.getProperties(), true);
			mxGraphStructure.setDefaultGraphStyle(aGraph, false);
		}
		else
		{
			throw new StructuralException("The graph is not a tree");
		}
	};

	/**
	 * @param aGraph
	 * @param vertexOne
	 * @param vertexTwo
	 * @return an edge that directly connects vertexOne and vertexTwo regardless of direction, null if they are not connected directly
	 */
	public static Object getConnectingEdge(mxAnalysisGraph aGraph, Object vertexOne, Object vertexTwo)
	{
		mxIGraphModel model = aGraph.getGraph().getModel();
		Object[] edges = aGraph.getEdges(vertexOne, null, true, true, false, true);

		for (int i = 0; i < edges.length; i++)
		{
			Object currEdge = edges[i];
			Object source = model.getTerminal(currEdge, true);
			Object target = model.getTerminal(currEdge, false);

			if (source.equals(vertexOne) && target.equals(vertexTwo))
			{
				return currEdge;

			}

			if (source.equals(vertexTwo) && target.equals(vertexOne))
			{
				return currEdge;
			}
		}

		return null;
	};

	/**
	 * @param graph
	 * @return Returns true if the graph has at least one cycle, taking edge direction into account
	 */
	public static boolean isCyclicDirected(mxAnalysisGraph aGraph)
	{
		mxGraph graph = aGraph.getGraph();
		mxIGraphModel model = graph.getModel();
		Object[] cells = model.cloneCells(aGraph.getChildCells(graph.getDefaultParent(), true, true), true);
		mxGraphModel modelCopy = new mxGraphModel();
		mxGraph graphCopy = new mxGraph(modelCopy);
		Object parentCopy = graphCopy.getDefaultParent();
		graphCopy.addCells(cells);
		mxAnalysisGraph aGraphCopy = new mxAnalysisGraph();
		aGraphCopy.setGraph(graphCopy);
		aGraphCopy.setGenerator(aGraph.getGenerator());
		aGraphCopy.setProperties(aGraph.getProperties());

		Object[] leaf = new Object[1];

		do
		{
			leaf[0] = getDirectedLeaf(aGraphCopy, parentCopy);

			if (leaf[0] != null)
			{
				graphCopy.removeCells(leaf);
			}
		}
		while (leaf[0] != null);

		int vertexNum = aGraphCopy.getChildVertices(parentCopy).length;

		if (vertexNum > 0)
		{
			return true;
		}
		else
		{
			return false;
		}
	};

	/**
	 * @param graph
	 * @param parent
	 * @param properties
	 * @return A helper function for isDirectedCyclic and it isn't for general use. It returns a node that hasn't incoming or outgoing edges. It could be considered a "leaf" in a directed graph, but this definition isn't formal.
	 */
	public static Object getDirectedLeaf(mxAnalysisGraph aGraph, Object parent)
	{
		Object[] vertices = aGraph.getChildVertices(parent);
		int vertexNum = vertices.length;
		Object currVertex;

		for (int i = 0; i < vertexNum; i++)
		{
			currVertex = vertices[i];
			int inEdgeCount = aGraph.getEdges(currVertex, parent, true, false, false, true).length;
			int outEdgeCount = aGraph.getEdges(currVertex, parent, false, true, false, true).length;

			if (outEdgeCount == 0 || inEdgeCount == 0)
			{
				return currVertex;
			}
		}

		return null;
	};

	/**
	 * Makes the complement of aGraph
	 * @param aGraph
	 */
	public static void complementaryGraph(mxAnalysisGraph aGraph)
	{
		ArrayList<ArrayList<mxCell>> oldConnections = new ArrayList<ArrayList<mxCell>>();
		mxGraph graph = aGraph.getGraph();
		Object parent = graph.getDefaultParent();
		//replicate the edge connections in oldConnections
		Object[] vertices = aGraph.getChildVertices(parent);
		int vertexCount = vertices.length;

		for (int i = 0; i < vertexCount; i++)
		{
			mxCell currVertex = (mxCell) vertices[i];
			int edgeCount = currVertex.getEdgeCount();
			mxCell currEdge = new mxCell();
			ArrayList<mxCell> neighborVertexes = new ArrayList<mxCell>();

			for (int j = 0; j < edgeCount; j++)
			{
				currEdge = (mxCell) currVertex.getEdgeAt(j);

				mxCell source = (mxCell) currEdge.getSource();
				mxCell destination = (mxCell) currEdge.getTarget();

				if (!source.equals(currVertex))
				{
					neighborVertexes.add(j, source);
				}
				else
				{
					neighborVertexes.add(j, destination);
				}

			}

			oldConnections.add(i, neighborVertexes);
		}

		//delete all edges and make a complementary model
		Object[] edges = aGraph.getChildEdges(parent);
		graph.removeCells(edges);

		for (int i = 0; i < vertexCount; i++)
		{
			ArrayList<mxCell> oldNeighbors = new ArrayList<mxCell>();
			oldNeighbors = oldConnections.get(i);
			mxCell currVertex = (mxCell) vertices[i];

			for (int j = 0; j < vertexCount; j++)
			{
				mxCell targetVertex = (mxCell) vertices[j];
				boolean shouldConnect = true; // the decision if the two current vertexes should be connected

				if (oldNeighbors.contains(targetVertex))
				{
					shouldConnect = false;
				}
				else if (targetVertex.equals(currVertex))
				{
					shouldConnect = false;
				}
				else if (areConnected(aGraph, currVertex, targetVertex))
				{
					shouldConnect = false;
				}

				if (shouldConnect)
				{
					graph.insertEdge(parent, null, null, currVertex, targetVertex);
				}
			}

		}
	};

	/**
	 * @param aGraph - the graph to search
	 * @param value - desired value
	 * @return the first vertex with the wanted value. If none are found, null is returned
	 */
	public static Object getVertexWithValue(mxAnalysisGraph aGraph, int value)
	{
		mxGraph graph = aGraph.getGraph();

		Object[] vertices = aGraph.getChildVertices(aGraph.getGraph().getDefaultParent());

		int childNum = vertices.length;
		int vertexValue = 0;
		mxCostFunction costFunction = aGraph.getGenerator().getCostFunction();
		mxGraphView view = graph.getView();

		for (int i = 0; i < childNum; i++)
		{
			Object currVertex = vertices[i];

			vertexValue = (int) costFunction.getCost(new mxCellState(view, currVertex, null));

			if (vertexValue == value)
			{
				return currVertex;
			}
		}
		return null;
	};

	/**
	 * Sets the style of the graph to that as in GraphEditor
	 * @param aGraph
	 * @param resetEdgeValues - set to true if you want to re-generate edge weights
	 */
	public static void setDefaultGraphStyle(mxAnalysisGraph aGraph, boolean resetEdgeValues)
	{
		mxGraph graph = aGraph.getGraph();
		Object parent = graph.getDefaultParent();
		Object[] vertices = aGraph.getChildVertices(parent);
		mxIGraphModel model = graph.getModel();

		for (int i = 0; i < vertices.length; i++)
		{
			model.setStyle(vertices[i], basicVertexStyleString);
		}

		Object[] edges = aGraph.getChildEdges(parent);
		boolean isDirected = mxGraphProperties.isDirected(aGraph.getProperties(), mxGraphProperties.DEFAULT_DIRECTED);
		String edgeString = basicEdgeStyleString;

		if (isDirected)
		{
			edgeString = edgeString + basicArrowStyleString;
		}
		else
		{
			edgeString = edgeString + "endArrow=none";
		}

		for (int i = 0; i < edges.length; i++)
		{
			model.setStyle(edges[i], edgeString);
		}

		if (resetEdgeValues)
		{
			for (int i = 0; i < edges.length; i++)
			{
				model.setValue(edges[i], null);
			}

			for (int i = 0; i < edges.length; i++)
			{
				model.setValue(edges[i], aGraph.getGenerator().getNewEdgeValue(aGraph));
			}
		}
	};

	/**
	 * @param aGraph
	 * @return the regularity of the graph
	 * @throws StructuralException if the graph is irregular
	 */
	public static int regularity(mxAnalysisGraph aGraph) throws StructuralException
	{
		mxGraph graph = aGraph.getGraph();
		Object[] vertices = aGraph.getChildVertices(graph.getDefaultParent());
		int vertexCount = vertices.length;
		Object currVertex = vertices[0];
		int regularity = aGraph.getEdges(currVertex, null, true, true).length;

		for (int i = 1; i < vertexCount; i++)
		{
			currVertex = vertices[i];

			if (regularity != aGraph.getEdges(currVertex, null, true, true).length)
			{
				throw new StructuralException("The graph is irregular.");
			}
		}

		return regularity;
	};

	/**
	 * @param aGraph
	 * @param vertex
	 * @return indegree of vertex
	 */
	public static int indegree(mxAnalysisGraph aGraph, Object vertex)
	{
		if (vertex == null)
		{
			throw new IllegalArgumentException();
		}

		if (mxGraphProperties.isDirected(aGraph.getProperties(), mxGraphProperties.DEFAULT_DIRECTED))
		{
			return aGraph.getEdges(vertex, aGraph.getGraph().getDefaultParent(), true, false, true, true).length;
		}
		else
		{
			return aGraph.getEdges(vertex, aGraph.getGraph().getDefaultParent(), true, true, true, true).length;
		}
	};

	/**
	 * @param aGraph
	 * @param vertex
	 * @return outdegree of vertex
	 */
	public static int outdegree(mxAnalysisGraph aGraph, Object vertex)
	{
		if (mxGraphProperties.isDirected(aGraph.getProperties(), mxGraphProperties.DEFAULT_DIRECTED))
		{
			return aGraph.getEdges(vertex, aGraph.getGraph().getDefaultParent(), false, true, true, true).length;
		}
		else
		{
			return aGraph.getEdges(vertex, aGraph.getGraph().getDefaultParent(), true, true, true, true).length;
		}
	};

	/**
	 * @param aGraph
	 * @param vertex
	 * @return true if vertex is a cut vertex
	 */
	public static boolean isCutVertex(mxAnalysisGraph aGraph, Object vertex)
	{
		mxGraph graph = aGraph.getGraph();
		mxIGraphModel model = graph.getModel();

		if (aGraph.getEdges(vertex, null, true, true, false, true).length >= 2)
		{
			Object[] cells = model.cloneCells(aGraph.getChildCells(graph.getDefaultParent(), true, true), true);
			mxGraphModel modelCopy = new mxGraphModel();
			mxGraph graphCopy = new mxGraph(modelCopy);
			graphCopy.addCells(cells);
			mxAnalysisGraph aGraphCopy = new mxAnalysisGraph();
			aGraphCopy.setGraph(graphCopy);
			aGraphCopy.setGenerator(aGraph.getGenerator());
			aGraphCopy.setProperties(aGraph.getProperties());

			Object newVertex = getVertexWithValue(aGraphCopy,
					(int) aGraph.getGenerator().getCostFunction().getCost(new mxCellState(graph.getView(), vertex, null)));

			graphCopy.removeCells(new Object[] { newVertex }, true);
			Object[][] oldComponents = getGraphComponents(aGraph);
			Object[][] newComponents = getGraphComponents(aGraphCopy);

			if (newComponents.length > oldComponents.length)
			{
				return true;
			}
		}

		return false;
	};

	/**
	 * @param aGraph
	 * @return all cut vertices of aGraph
	 */
	public static Object[] getCutVertices(mxAnalysisGraph aGraph)
	{
		ArrayList<Object> cutVertexList = new ArrayList<Object>();
		Object[] vertexes = aGraph.getChildVertices(aGraph.getGraph().getDefaultParent());
		int vertexNum = vertexes.length;

		for (int i = 0; i < vertexNum; i++)
		{
			if (isCutVertex(aGraph, vertexes[i]))
			{
				cutVertexList.add(vertexes[i]);
			}
		}

		return cutVertexList.toArray();
	};

	/**
	 * @param aGraph
	 * @param edge
	 * @return true if edge is a cut edge of aGraph
	 */
	public static boolean isCutEdge(mxAnalysisGraph aGraph, Object edge)
	{
		mxGraph graph = aGraph.getGraph();
		mxIGraphModel model = graph.getModel();
		mxCostFunction costFunction = aGraph.getGenerator().getCostFunction();
		mxGraphView view = graph.getView();

		int srcValue = (int) costFunction.getCost(new mxCellState(view, aGraph.getTerminal(edge, true), null));
		int destValue = (int) costFunction.getCost(new mxCellState(view, aGraph.getTerminal(edge, false), null));

		if (aGraph.getTerminal(edge, false) != null || aGraph.getTerminal(edge, true) != null)
		{
			Object[] cells = model.cloneCells(aGraph.getChildCells(graph.getDefaultParent(), true, true), true);
			mxGraphModel modelCopy = new mxGraphModel();
			mxGraph graphCopy = new mxGraph(modelCopy);
			graphCopy.addCells(cells);
			mxAnalysisGraph aGraphCopy = new mxAnalysisGraph();
			aGraphCopy.setGraph(graphCopy);
			aGraphCopy.setGenerator(aGraph.getGenerator());
			aGraphCopy.setProperties(aGraph.getProperties());

			Object[] edges = aGraphCopy.getChildEdges(aGraphCopy.getGraph().getDefaultParent());
			Object currEdge = edges[0];
			mxCostFunction costFunctionCopy = aGraphCopy.getGenerator().getCostFunction();
			mxGraphView viewCopy = graphCopy.getView();

			int currSrcValue = (int) costFunctionCopy.getCost(new mxCellState(viewCopy, aGraphCopy.getTerminal(currEdge, true), null));
			int currDestValue = (int) costFunctionCopy.getCost(new mxCellState(viewCopy, aGraphCopy.getTerminal(currEdge, false), null));
			int i = 0;

			while (currSrcValue != srcValue || currDestValue != destValue)
			{
				i++;
				currEdge = edges[i];
				currSrcValue = Integer.parseInt((String) modelCopy.getValue(aGraphCopy.getTerminal(currEdge, true)));
				currDestValue = Integer.parseInt((String) modelCopy.getValue(aGraphCopy.getTerminal(currEdge, false)));
			}

			graphCopy.removeCells(new Object[] { currEdge }, true);
			Object[][] oldComponents = getGraphComponents(aGraph);
			Object[][] newComponents = getGraphComponents(aGraphCopy);

			if (newComponents.length > oldComponents.length)
			{
				return true;
			}
		}

		return false;
	};

	/**
	 * @param aGraph
	 * @return all cut edges of aGraph
	 */
	public static Object[] getCutEdges(mxAnalysisGraph aGraph)
	{
		ArrayList<Object> cutEdgeList = new ArrayList<Object>();
		Object[] edges = aGraph.getChildEdges(aGraph.getGraph().getDefaultParent());
		int edgeNum = edges.length;

		for (int i = 0; i < edgeNum; i++)
		{
			if (isCutEdge(aGraph, edges[i]))
			{
				cutEdgeList.add(edges[i]);
			}
		}

		return cutEdgeList.toArray();
	};

	/**
	 * @param aGraph
	 * @return all source vertices of aGraph
	 * @throws StructuralException the graph must be directed
	 */
	public static Object[] getSourceVertices(mxAnalysisGraph aGraph) throws StructuralException
	{
		if (!mxGraphProperties.isDirected(aGraph.getProperties(), mxGraphProperties.DEFAULT_DIRECTED))
		{
			throw new StructuralException("The graph is undirected, so it can't have source vertices.");
		}

		ArrayList<Object> sourceList = new ArrayList<Object>();
		Object[] vertices = aGraph.getChildVertices(aGraph.getGraph().getDefaultParent());

		for (int i = 0; i < vertices.length; i++)
		{
			Object currVertex = vertices[i];
			Object[] outEdges = aGraph.getEdges(vertices[i], null, false, true, true, true);
			Object[] inEdges = aGraph.getEdges(vertices[i], null, true, false, true, true);

			if (inEdges.length == 0 && outEdges.length > 0)
			{
				sourceList.add(currVertex);
			}
		}

		return sourceList.toArray();
	};

	/**
	 * @param aGraph
	 * @return all sink vertices of aGraph
	 * @throws StructuralException the graph must be directed
	 */
	public static Object[] getSinkVertices(mxAnalysisGraph aGraph) throws StructuralException
	{
		if (!mxGraphProperties.isDirected(aGraph.getProperties(), mxGraphProperties.DEFAULT_DIRECTED))
		{
			throw new StructuralException("The graph is undirected, so it can't have sink vertices.");
		}

		ArrayList<Object> sourceList = new ArrayList<Object>();
		Object[] vertices = aGraph.getChildVertices(aGraph.getGraph().getDefaultParent());

		for (int i = 0; i < vertices.length; i++)
		{
			Object currVertex = vertices[i];
			Object[] outEdges = aGraph.getEdges(vertices[i], null, false, true, true, true);
			Object[] inEdges = aGraph.getEdges(vertices[i], null, true, false, true, true);

			if (inEdges.length > 0 && outEdges.length == 0)
			{
				sourceList.add(currVertex);
			}
		}

		return sourceList.toArray();
	};

	/**
	 * @param aGraph
	 * @return true if aGraph is biconnected
	 */
	public static boolean isBiconnected(mxAnalysisGraph aGraph)
	{
		int edgeCount = aGraph.getChildEdges(aGraph.getGraph().getDefaultParent()).length;

		if (getCutVertices(aGraph).length == 0 && edgeCount >= 1)
		{
			return true;
		}
		else
		{
			return false;
		}
	};
};

redpajama_set_name: RedPajamaGithub


text:
package biz;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import basic.UiThreadHandler;

public class CmdExecutor {

	public static ExecutorService executorService = Executors
			.newCachedThreadPool();

	public static void exeInBackground(iCmd iCmd, iCmdListener iCmdListener) {
		// TODO Auto-generated method stub
		executorService.execute(new CmdWrapper(iCmd, iCmdListener));

	}

	public static void exeInFroground(iCmd iCmd, iCmdListener iCmdListener) {
		// TODO Auto-generated method stub
//		executorService.execute(new CmdWrapper(iCmd, iCmdListener));
		iCmdListener.onPreExe();
		if (iCmd.exe()) {
			iCmdListener.onSucess();
		} else {
			iCmdListener.onFail();
		}

	}

	private static class CmdWrapper implements Runnable {
		iCmd cmd;
		iCmdListener iCmdListener;

		public CmdWrapper(iCmd cmd, biz.iCmdListener iCmdListener) {
			// super();
			this.cmd = cmd;
			this.iCmdListener = iCmdListener;
		}

		public void run() {
			UiThreadHandler.post(new Runnable() {

				@Override
				public void run() {
					// TODO Auto-generated method stub
					iCmdListener.onPreExe();
				}
			});

			if (cmd.exe()) {
				UiThreadHandler.post(new Runnable() {

					@Override
					public void run() {
						// TODO Auto-generated method stub
						iCmdListener.onSucess();
					}
				});

			} else {
				UiThreadHandler.post(new Runnable() {

					@Override
					public void run() {
						// TODO Auto-generated method stub
						iCmdListener.onFail();
					}
				});

			}

		}
	}

}


text:
// Copyright (C) Pash Contributors. License: GPL/BSD. See https://github.com/Pash-Project/Pash/
using System;
using System.Collections.Generic;
using System.Text;
using Pash.Implementation;

namespace System.Management.Automation.Provider
{
    public abstract class ItemCmdletProvider : DriveCmdletProvider
    {
        protected ItemCmdletProvider()
        {
        }

        protected virtual void ClearItem(Path path) { throw new NotImplementedException(); }
        protected virtual object ClearItemDynamicParameters(Path path) { throw new NotImplementedException(); }
        protected virtual void GetItem(Path path) { throw new NotImplementedException(); }
        protected virtual object GetItemDynamicParameters(Path path) { throw new NotImplementedException(); }
        protected virtual void InvokeDefaultAction(Path path) { throw new NotImplementedException(); }
        protected virtual object InvokeDefaultActionDynamicParameters(Path path) { throw new NotImplementedException(); }
        protected abstract bool IsValidPath(Path path);
        protected virtual bool ItemExists(Path path) { throw new NotImplementedException(); }
        protected virtual object ItemExistsDynamicParameters(Path path) { throw new NotImplementedException(); }
        protected virtual void SetItem(Path path, object value) { throw new NotImplementedException(); }
        protected virtual object SetItemDynamicParameters(Path path, object value) { throw new NotImplementedException(); }

        // internals
        //internal void ClearItem(Path path, CmdletProviderContext context);
        //internal object ClearItemDynamicParameters(Path path, CmdletProviderContext context);
        //internal void GetItem(Path path, CmdletProviderContext context);
        //internal object GetItemDynamicParameters(Path path, CmdletProviderContext context);
        //internal void InvokeDefaultAction(Path path, CmdletProviderContext context);
        //internal object InvokeDefaultActionDynamicParameters(Path path, CmdletProviderContext context);
        //internal bool IsValidPath(Path path, CmdletProviderContext context);
        //internal bool ItemExists(Path path, CmdletProviderContext context);
        //internal object ItemExistsDynamicParameters(Path path, CmdletProviderContext context);
        //internal void SetItem(Path path, object value, CmdletProviderContext context);
        //internal object SetItemDynamicParameters(Path path, object value, CmdletProviderContext context);

        internal bool ItemExists(Path path, ProviderRuntime providerRuntime)
        {
            ProviderRuntime = providerRuntime;

            return ItemExists(path);
        }

        internal void GetItem(Path path, ProviderRuntime providerRuntime)
        {
            ProviderRuntime = providerRuntime;
            GetItem(path);
        }
    }
}


text:
SYNONYM

#### According to
Index Fungorum

#### Published in
null

#### Original name
Irpex farinaceous Fr.

### Remarks
null


text:
<?php

namespace DTS\eBaySDK\Trading\Types\Test;

use DTS\eBaySDK\Trading\Types\GetTaxTableRequestType;

class GetTaxTableRequestTypeTest extends \PHPUnit_Framework_TestCase
{
    private $obj;

    protected function setUp()
    {
        $this->obj = new GetTaxTableRequestType();
    }

    public function testCanBeCreated()
    {
        $this->assertInstanceOf('\DTS\eBaySDK\Trading\Types\GetTaxTableRequestType', $this->obj);
    }

    public function testExtendsAbstractRequestType()
    {
        $this->assertInstanceOf('\DTS\eBaySDK\Trading\Types\AbstractRequestType', $this->obj);
    }
}


text:
package com.tiny.todomvptiny.tasks;

import android.app.Activity;
import android.support.annotation.NonNull;
import android.util.Log;

import com.tiny.todomvptiny.addedittask.AddEditTaskActivity;
import com.tiny.todomvptiny.data.Task;
import com.tiny.todomvptiny.data.source.TasksDataSource;
import com.tiny.todomvptiny.data.source.TasksRepository;

import java.util.ArrayList;
import java.util.List;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Created by tiny on 17/1/12.
 * Listens to user actions from the UI ({@link TasksFragment}), retrieves the data and updates the
 * UI as required.
 */

public class TasksPresenter implements TasksContract.Presenter {
    private final TasksRepository mTasksRepository;

    private final TasksContract.View mTasksView;

    private TasksFilterType mCurrentFiltering = TasksFilterType.ALL_TASKS;

    private boolean mFirstLoad = true;

    public TasksPresenter(TasksRepository mTasksRepository, TasksContract.View mTasksView) {
        this.mTasksRepository = checkNotNull(mTasksRepository, "tasksRepository cannot be null!");
        this.mTasksView = checkNotNull(mTasksView, "tasksView cannot be null!");

        mTasksView.setPresenter(this);
    }

    @Override
    public void start() {
        loadTasks(false);
    }

    @Override
    public void result(int requestCode, int resultCode) {
        //If a task was successfully added. show snackbar.
        if (AddEditTaskActivity.REQUEST_ADD_TASK == requestCode && Activity.RESULT_OK == resultCode) {
            mTasksView.showSuccessfullySavedMessage();
        }
    }

    @Override
    public void loadTasks(boolean forceUpdate) {
        //Simplification for sample: a network reload will be forced on first load.
        loadTasks(forceUpdate || mFirstLoad, true);
        mFirstLoad = false;
    }

    private void loadTasks(boolean forceUpdate, final boolean showLoadingUI) {
        if (showLoadingUI) {
            mTasksView.setLoadingIndicator(true);
        }

        if (forceUpdate) {
            mTasksRepository.refreshTasks();
        }

        //The network request might be handled in a different thread so make sure Espresso knows
        //that the app is busy until the response is handled.

        mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() {
            @Override
            public void onTaskLoaded(List<Task> tasks) {
                List<Task> tasksToShow = new ArrayList<Task>();

                //We filter the tasks based on the requestType
                for (Task task : tasks) {
                    switch (mCurrentFiltering) {
                        case ALL_TASKS:
                            tasksToShow.add(task);
                            break;
                        case ACTIVE_TASKS:
                            if (task.isActive()) {
                                tasksToShow.add(task);
                            }
                            break;
                        case COMPLETED_TASKS:
                            if (task.isCompleted()) {
                                tasksToShow.add(task);
                            }
                            break;
                        default:
                            tasksToShow.add(task);
                            break;
                    }
                }
                //The view may not be able to handle UI update anymore
                if (!mTasksView.isActive()) {
                    return;
                }

                //记载完数据之后,让加载对话框消失掉
                if (showLoadingUI) {
                    mTasksView.setLoadingIndicator(false);
                }

                processTasks(tasksToShow);
            }

            @Override
            public void onDataNoAvailable() {
                //The view may not be able to handle UI updates anymore
                if (!mTasksView.isActive()) {
                    return;
                }
                mTasksView.showLoadingTasksError();
            }
        });
    }

    private void processTasks(List<Task> tasks) {
        if (tasks.isEmpty()) {
            // Show a message indicating there are no tasks for that filter type.
            processEmptyTasks();
        } else {
            // SHow the list of tasks.
            mTasksView.showTasks(tasks);
            // Set the filter label's text.
            showFilterLabel();
        }
    }

    private void showFilterLabel() {
        Log.e("showFilterLabel","mCurrentFiltering --> " + mCurrentFiltering.toString());
        switch (mCurrentFiltering) {
            case ACTIVE_TASKS:
                mTasksView.showActiveFilterLabel();
                break;
            case COMPLETED_TASKS:
                mTasksView.showCompletedFilterLabel();
                break;
            default:
                mTasksView.showAllFilterLabel();
                break;
        }
    }

    private void processEmptyTasks() {
        Log.e("processEmptyTasks","mCurrentFiltering --> " + mCurrentFiltering.toString());
        switch (mCurrentFiltering) {
            case ACTIVE_TASKS:
                mTasksView.showNoActiveTasks();
                break;
            case COMPLETED_TASKS:
                mTasksView.showNoCompletedTasks();
                break;
            default:
                mTasksView.showNoTasks();
                break;
        }
    }

    @Override
    public void addNewTasks() {
        mTasksView.showAddTask();
    }

    @Override
    public void openTaskDetails(@NonNull Task requestedTask) {
        checkNotNull(requestedTask, "requestedTask cannot be null!");
        mTasksView.showTaskDetailsUi(requestedTask.getId());
    }

    @Override
    public void completeTask(@NonNull Task completedTask) {
        checkNotNull(completedTask, "completedTask cannot be null!");
        mTasksRepository.completeTask(completedTask);
        mTasksView.showTaskMarkedComplete();
        loadTasks(false, false);
    }

    @Override
    public void activateTask(@NonNull Task activeTask) {
        checkNotNull(activeTask, "activeTask cannot be null!");
        mTasksRepository.activateTask(activeTask);
        mTasksView.showTasksMarkedActive();
        loadTasks(false, false);
    }

    @Override
    public void clearCompletedTasks() {
        mTasksRepository.clearCompletedTasks();
        mTasksView.showCompletedTasksCleared();
        loadTasks(false,false);
    }

    @Override
    public void setFiltering(TasksFilterType requestType) {
        mCurrentFiltering = requestType;
    }

    @Override
    public TasksFilterType getFiltering() {
        return mCurrentFiltering;
    }

}


text:
package de.danoeh.antennapod.config;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;

import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.GpodnetCallbacks;

public class GpodnetCallbacksImpl implements GpodnetCallbacks {
    @Override
    public boolean gpodnetEnabled() {
        return true;
    }

    @Override
    public PendingIntent getGpodnetSyncServiceErrorNotificationPendingIntent(Context context) {
        return PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class),
                PendingIntent.FLAG_UPDATE_CURRENT);
    }
}


text: Elasticsearch duomenų importavimas ir masažavimas.


text:
<?php

namespace FSi\Component\DataSource\Tests\Extension\Core;

use FSi\Component\DataSource\DataSourceFactory;
use FSi\Component\DataSource\Driver\Collection\CollectionFactory;
use FSi\Component\DataSource\Driver\Collection\Extension\Core\CoreExtension;
use FSi\Component\DataSource\Driver\DriverFactoryManager;
use FSi\Component\DataSource\Extension\Core\Pagination\PaginationExtension;
use FSi\Component\DataSource\Event\DataSourceEvent;
use PHPUnit\Framework\TestCase;
use FSi\Component\DataSource\Driver\DriverInterface;
use FSi\Component\DataSource\DataSource;
use FSi\Component\DataSource\DataSourceViewInterface;

class PaginationExtensionTest extends TestCase
{
    public function paginationCases(): array
    {
        return [
            [
                'first_result' => 20,
                'max_results' => 20,
                'page' => 2,
                'current_page' => 2
            ],
            [
                'first_result' => 20,
                'max_results' => 0,
                'page' => null,
                'current_page' => 1
            ],
            [
                'first_result' => 0,
                'max_results' => 20,
                'page' => null,
                'current_page' => 1
            ],
        ];
    }

    /**
     * First case of event (when page is not 1).
     * @dataProvider paginationCases
     */
    public function testPaginationExtension(int $firstResult, int $maxResults, ?int $page, int $currentPage): void
    {
        $driver = $this->createMock(DriverInterface::class);
        $extension = new PaginationExtension();

            $datasource = $this->getMockBuilder(DataSource::class)->setConstructorArgs([$driver])->getMock();

            $datasource->method('getName')->willReturn('datasource');
            $datasource->method('getResult')->willReturn([]);
            $datasource->method('getMaxResults')->willReturn($maxResults);
            $datasource->method('getFirstResult')->willReturn($firstResult);

            $subscribers = $extension->loadSubscribers();
            $subscriber = array_shift($subscribers);
            $event = new DataSourceEvent\ParametersEventArgs($datasource, []);
            $subscriber->postGetParameters($event);

        if (null !== $page) {
            self::assertSame(
                [
                    'datasource' => [
                        PaginationExtension::PARAMETER_MAX_RESULTS => 20,
                        PaginationExtension::PARAMETER_PAGE => 2
                    ]
                ],
                $event->getParameters()
            );
        } else {
            $parameters = $event->getParameters();
            if (isset($parameters['datasource'])) {
                self::assertArrayNotHasKey(PaginationExtension::PARAMETER_PAGE, $parameters['datasource']);
            }
        }

            $view = $this->createMock(DataSourceViewInterface::class);
            $view->method('setAttribute')
                ->willReturnCallback(
                    function ($attribute, $value) use ($currentPage) {
                        if ('page' === $attribute) {
                            self::assertEquals($currentPage, $value);
                        }
                    }
                )
            ;

            $subscriber->postBuildView(new DataSourceEvent\ViewEventArgs($datasource, $view));
    }

    public function testSetMaxResultsByBindRequest(): void
    {
        $extensions = [
            new PaginationExtension()
        ];
        $driverExtensions = [new CoreExtension()];
        $driverFactory = new CollectionFactory($driverExtensions);
        $driverFactoryManager = new DriverFactoryManager([$driverFactory]);
        $factory = new DataSourceFactory($driverFactoryManager, $extensions);
        $dataSource = $factory->createDataSource('collection', [], 'foo_source');

        $dataSource->bindParameters([
            'foo_source' => [
                PaginationExtension::PARAMETER_MAX_RESULTS => 105
            ]
        ]);

        self::assertEquals(105, $dataSource->getMaxResults());
    }
}


text:
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision: 322138 $ -->
<refentry xml:id="tag.getyear" xmlns="http://docbook.org/ns/docbook">
  <refnamediv>
    <refname>KTaglib_Tag::getYear</refname>
    <refpurpose>Returns the year from a ID3 tag</refpurpose>
  </refnamediv>
  <refsect1 role="description">
    &reftitle.description;
    <methodsynopsis>
      <modifier>public</modifier>
      <type>int</type>
      <methodname>KTaglib_Tag::getYear</methodname>
      <void />
    </methodsynopsis>
    <para>
      Returns the year of an ID3 tag. This method is implemented
      in ID3v1 and ID3v2 tags.
    </para>
  </refsect1>
 <refsect1 role="returnvalues">
  &reftitle.returnvalues;
  <para>
    Returns the year as an integer
  </para>
 </refsect1>

</refentry>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->


text:
package org.kohsuke.github;

import org.junit.Test;

import static org.hamcrest.Matchers.*;

public class GHObjectTest extends org.kohsuke.github.AbstractGitHubWireMockTest {

    @Test
    public void test_toString() throws Exception {
        GHOrganization org = gitHub.getOrganization(GITHUB_API_TEST_ORG);
        assertThat(org.toString(),
                containsString(
                        "login=hub4j-test-org,location=<null>,blog=<null>,email=<null>,bio=<null>,name=<null>,company=<null>,type=Organization,followers=0,following=0,hireable=false"));

        // getResponseHeaderFields is deprecated but we should not break it.
        assertThat(org.getResponseHeaderFields(), notNullValue());
        assertThat(org.getResponseHeaderFields().get("Cache-Control").get(0), is("private, max-age=60, s-maxage=60"));
    }
}