public DataCell[] generateOutputRow(DataCell[] cells) throws RJException {
    Map row = new HashMap();
    for (int i = 0; i < inputFormat.length; i++) {
      row.put(inputFormat[i], cells[i]);
    }

    for (Iterator conv = evaluationStages.iterator(); conv.hasNext(); ) {
      AbstractColumnConverter converter = (AbstractColumnConverter) conv.next();
      DataColumnDefinition[] expected = converter.getExpectedColumns();
      DataColumnDefinition[] produced = converter.getOutputColumns();
      DataCell[] input = new DataCell[expected.length];
      for (int i = 0; i < input.length; i++) {
        input[i] = (DataCell) row.get(expected[i]);
      }
      DataCell[] output = converter.convert(input);
      for (int i = 0; i < output.length; i++) {
        row.put(produced[i], output[i]);
      }
    }

    DataCell[] out = new DataCell[outputFormat.length];
    for (int i = 0; i < out.length; i++) {
      out[i] = (DataCell) row.get(outputFormat[i]);
    }

    if (logLevel >= 2) {
      Log.log(ModelGenerator.class, "Columns before convert: " + PrintUtils.printArray(cells), 2);
      Log.log(ModelGenerator.class, "Columns after convert: " + PrintUtils.printArray(out), 2);
    }
    return out;
  }
  public ModelGenerator(AbstractColumnConverter[] converters) {
    Log.log(ModelGenerator.class, "Creating new model generator", 1);
    List output = new ArrayList();
    List input = new ArrayList();
    Map available = new HashMap();
    for (int i = 0; i < converters.length; i++) {
      DataColumnDefinition[] in = converters[i].getExpectedColumns();
      DataColumnDefinition[] out = converters[i].getOutputColumns();
      for (int j = 0; j < in.length; j++) {
        if (!(in[j] instanceof ConverterColumnWrapper)) {
          if (!input.contains(in[j])) {
            input.add(in[j]);
          }
          //					if (!output.contains(in[j])) {
          //						output.add(in[j]);
          //					}
          available.put(in[j], null);
        }
      }
      for (int j = 0; j < out.length; j++) {
        if (!output.contains(out[j])) {
          output.add(out[j]);
        }
      }
    }
    this.inputFormat = (DataColumnDefinition[]) input.toArray(new DataColumnDefinition[] {});
    this.outputFormat = (DataColumnDefinition[]) output.toArray(new DataColumnDefinition[] {});

    this.sortedOutput = new DataColumnDefinition[outputFormat.length];
    for (int i = 0; i < sortedOutput.length; i++) {
      sortedOutput[i] = outputFormat[i];
    }
    Arrays.sort(
        sortedOutput,
        new Comparator() {
          public int compare(Object arg0, Object arg1) {
            return ((DataColumnDefinition) arg0)
                .getColumnName()
                .toLowerCase()
                .compareTo(((DataColumnDefinition) arg1).getColumnName().toLowerCase());
          }
        });

    List toDistribute = new ArrayList();
    for (int i = 0; i < converters.length; i++) {
      toDistribute.add(converters[i]);
    }

    int id = 0;
    main:
    while (!toDistribute.isEmpty()) {
      AbstractColumnConverter converter = (AbstractColumnConverter) toDistribute.get(id);
      for (int i = 0; i < converter.getExpectedColumns().length; i++) {
        if (!available.containsKey(converter.getExpectedColumns()[i])) {
          id++;
          continue main;
        }
      }
      toDistribute.remove(id);
      id = 0;
      for (int i = 0; i < converter.getOutputColumns().length; i++) {
        available.put(converter.getOutputColumns()[i], null);
      }
      evaluationStages.add(converter);
    }

    if (logLevel >= 2) {
      Log.log(
          ModelGenerator.class, "Model input columns: " + PrintUtils.printArray(inputFormat), 2);
      Log.log(
          ModelGenerator.class, "Model output columns: " + PrintUtils.printArray(outputFormat), 2);
    }
  }
public class ModelGenerator {

  private static final int logLevel = Log.getLogLevel(ModelGenerator.class);

  private DataColumnDefinition[] inputFormat;
  private DataColumnDefinition[] outputFormat;
  private List evaluationStages = new ArrayList();

  private DataColumnDefinition[] sortedOutput;

  public ModelGenerator(DataColumnDefinition[] columns) {
    this.inputFormat = columns;
    this.outputFormat = new DataColumnDefinition[columns.length];
    for (int i = 0; i < columns.length; i++) {
      DummyConverter dummyConverter =
          new DummyConverter(columns[i].getColumnName(), columns[i], null);
      evaluationStages.add(dummyConverter);
      outputFormat[i] = dummyConverter.getOutputColumns()[0];
    }
  }

  public ModelGenerator(AbstractColumnConverter[] converters) {
    Log.log(ModelGenerator.class, "Creating new model generator", 1);
    List output = new ArrayList();
    List input = new ArrayList();
    Map available = new HashMap();
    for (int i = 0; i < converters.length; i++) {
      DataColumnDefinition[] in = converters[i].getExpectedColumns();
      DataColumnDefinition[] out = converters[i].getOutputColumns();
      for (int j = 0; j < in.length; j++) {
        if (!(in[j] instanceof ConverterColumnWrapper)) {
          if (!input.contains(in[j])) {
            input.add(in[j]);
          }
          //					if (!output.contains(in[j])) {
          //						output.add(in[j]);
          //					}
          available.put(in[j], null);
        }
      }
      for (int j = 0; j < out.length; j++) {
        if (!output.contains(out[j])) {
          output.add(out[j]);
        }
      }
    }
    this.inputFormat = (DataColumnDefinition[]) input.toArray(new DataColumnDefinition[] {});
    this.outputFormat = (DataColumnDefinition[]) output.toArray(new DataColumnDefinition[] {});

    this.sortedOutput = new DataColumnDefinition[outputFormat.length];
    for (int i = 0; i < sortedOutput.length; i++) {
      sortedOutput[i] = outputFormat[i];
    }
    Arrays.sort(
        sortedOutput,
        new Comparator() {
          public int compare(Object arg0, Object arg1) {
            return ((DataColumnDefinition) arg0)
                .getColumnName()
                .toLowerCase()
                .compareTo(((DataColumnDefinition) arg1).getColumnName().toLowerCase());
          }
        });

    List toDistribute = new ArrayList();
    for (int i = 0; i < converters.length; i++) {
      toDistribute.add(converters[i]);
    }

    int id = 0;
    main:
    while (!toDistribute.isEmpty()) {
      AbstractColumnConverter converter = (AbstractColumnConverter) toDistribute.get(id);
      for (int i = 0; i < converter.getExpectedColumns().length; i++) {
        if (!available.containsKey(converter.getExpectedColumns()[i])) {
          id++;
          continue main;
        }
      }
      toDistribute.remove(id);
      id = 0;
      for (int i = 0; i < converter.getOutputColumns().length; i++) {
        available.put(converter.getOutputColumns()[i], null);
      }
      evaluationStages.add(converter);
    }

    if (logLevel >= 2) {
      Log.log(
          ModelGenerator.class, "Model input columns: " + PrintUtils.printArray(inputFormat), 2);
      Log.log(
          ModelGenerator.class, "Model output columns: " + PrintUtils.printArray(outputFormat), 2);
    }
  }

  public DataColumnDefinition[] getInputFormat() {
    return inputFormat;
  }

  public DataColumnDefinition[] getOutputFormat() {
    return outputFormat;
  }

  public DataCell[] generateOutputRow(DataCell[] cells) throws RJException {
    Map row = new HashMap();
    for (int i = 0; i < inputFormat.length; i++) {
      row.put(inputFormat[i], cells[i]);
    }

    for (Iterator conv = evaluationStages.iterator(); conv.hasNext(); ) {
      AbstractColumnConverter converter = (AbstractColumnConverter) conv.next();
      DataColumnDefinition[] expected = converter.getExpectedColumns();
      DataColumnDefinition[] produced = converter.getOutputColumns();
      DataCell[] input = new DataCell[expected.length];
      for (int i = 0; i < input.length; i++) {
        input[i] = (DataCell) row.get(expected[i]);
      }
      DataCell[] output = converter.convert(input);
      for (int i = 0; i < output.length; i++) {
        row.put(produced[i], output[i]);
      }
    }

    DataCell[] out = new DataCell[outputFormat.length];
    for (int i = 0; i < out.length; i++) {
      out[i] = (DataCell) row.get(outputFormat[i]);
    }

    if (logLevel >= 2) {
      Log.log(ModelGenerator.class, "Columns before convert: " + PrintUtils.printArray(cells), 2);
      Log.log(ModelGenerator.class, "Columns after convert: " + PrintUtils.printArray(out), 2);
    }
    return out;
  }

  public AbstractColumnConverter[] getConverters() {
    return (AbstractColumnConverter[]) evaluationStages.toArray(new AbstractColumnConverter[] {});
  }

  public DataColumnDefinition[] getDependantColumns(DataColumnDefinition dataColumnDefinition) {
    List deps = new ArrayList();
    if (dataColumnDefinition instanceof ConverterColumnWrapper) {
      AbstractColumnConverter converter = null;
      main:
      for (int i = 0; i < evaluationStages.size(); i++) {
        AbstractColumnConverter conv = (AbstractColumnConverter) evaluationStages.get(i);
        for (int j = 0; j < conv.getOutputColumns().length; j++) {
          if (conv.getOutputColumns()[j].equals(dataColumnDefinition)) {
            converter = conv;
            break main;
          }
        }
      }
      for (int i = 0; i < converter.getExpectedColumns().length; i++) {
        deps.addAll(Arrays.asList(getDependantColumns(converter.getExpectedColumns()[i])));
      }
      return (DataColumnDefinition[]) deps.toArray(new DataColumnDefinition[] {});
    } else {
      return new DataColumnDefinition[] {dataColumnDefinition};
    }
  }

  public DataColumnDefinition getColumnByName(String string) {
    for (int i = 0; i < outputFormat.length; i++) {
      if (outputFormat[i].getColumnName().equals(string)) {
        return outputFormat[i];
      }
    }
    return null;
  }

  public DataColumnDefinition[] getSortedOutputColumns() {
    return sortedOutput;
  }
}