All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fit.RowFixture Maven / Gradle / Ivy

There is a newer version: 20181217
Show newest version
// Modified or written by Object Mentor, Inc. for inclusion with FitNesse.
// Copyright (c) 2002 Cunningham & Cunningham, Inc.
// Released under the terms of the GNU General Public License version 2 or later.
// Copyright (c) 2002 Cunningham & Cunningham, Inc.
// Released under the terms of the GNU General Public License version 2 or later.

package fit;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public abstract class RowFixture extends ColumnFixture {

  public Object[] results;
  public List missing = new LinkedList<>();
  public List surplus = new LinkedList<>();

  @Override
  public void doRows(Parse rows) {
    try {
      bindColumnHeadersToMethodsAndFields(rows.parts);
      results = query();
      match(list(rows.more), list(results), 0);
      Parse last = rows.last();
      last.more = buildRows(surplus.toArray());
      mark(last.more, "surplus");
      mark(missing.iterator(), "missing");
    }
    catch (Exception e) {
      exception(rows.leaf(), e);
    }
  }

  public abstract Object[] query() throws Exception;  // get rows to be compared

  @Override
  public abstract Class getTargetClass();             // get expected type of row

  protected void match(List expected, List computed, int col) {
    if (col >= columnBindings.length) {
      check(expected, computed);
    } else if (columnBindings[col] == null) {
      match(expected, computed, col + 1);
    } else {
      Map eMap = eSort(expected, col);
      Map cMap = cSort(computed, col);
      Set keys = union(eMap.keySet(), cMap.keySet());
      for (Object key : keys) {
        List eList = (List) eMap.get(key);
        List cList = (List) cMap.get(key);
        if (eList == null) {
          surplus.addAll(cList);
        } else if (cList == null) {
          missing.addAll(eList);
        } else if (eList.size() == 1 && cList.size() == 1) {
          check(eList, cList);
        } else {
          match(eList, cList, col + 1);
        }
      }
    }
  }

  protected List list(Parse rows) {
    List result = new LinkedList<>();
    while (rows != null) {
      result.add(rows);
      rows = rows.more;
    }
    return result;
  }

  protected List list(Object[] rows) {
    List result = new LinkedList<>();
    Collections.addAll(result, rows);
    return result;
  }

  protected Map eSort(List list, int col) {
    TypeAdapter a = columnBindings[col].adapter;
    Map result = new ConcurrentHashMap<>(list.size());
    for (Object o : list) {
      Parse row = (Parse) o;
      Parse cell = row.parts.at(col);
      try {
        Object key = a.parse(cell.text());
        bin(result, key, row);
      }
      catch (Exception e) {
        exception(cell, e);
        for (Parse rest = cell.more; rest != null; rest = rest.more) {
          ignore(rest);
        }
      }
    }
    return result;
  }

  protected Map cSort(List list, int col) {
    TypeAdapter a = columnBindings[col].adapter;
    Map result = new ConcurrentHashMap<>(list.size());
    for (Object row : list) {
      try {
        a.target = row;
        Object key = a.get();
        bin(result, key, row);
      }
      catch (Exception e) {
        // surplus anything with bad keys, including null
        surplus.add(row);
      }
    }
    return result;
  }

  protected void bin(Map result, Object key, Object row) {
    if (key.getClass().isArray()) {
      key = Arrays.asList((Object[]) key);
    }
    if (result.containsKey(key)) {
      ((List) result.get(key)).add(row);
    } else {
      List list = new LinkedList<>();
      list.add(row);
      result.put(key, list);
    }
  }

  protected Set union(Set a, Set b) {
    Set result = new HashSet<>();
    result.addAll(a);
    result.addAll(b);
    return result;
  }

  protected void check(List eList, List cList) {
    if (eList.isEmpty()) {
      surplus.addAll(cList);
      return;
    }
    if (cList.isEmpty()) {
      missing.addAll(eList);
      return;
    }
    Parse row = (Parse) eList.remove(0);
    Parse cell = row.parts;
    Object obj = cList.remove(0);
    for (int i = 0; i < columnBindings.length && cell != null; i++) {
      TypeAdapter a = columnBindings[i].adapter;
      if (a != null) {
        a.target = obj;
      }
      check(cell, a);
      cell = cell.more;
    }
    check(eList, cList);
  }

  protected void mark(Parse rows, String message) {
    String annotation = label(message);
    while (rows != null) {
      wrong(rows.parts);
      rows.parts.addToBody(annotation);
      rows = rows.more;
    }
  }

  protected void mark(Iterator rows, String message) {
    String annotation = label(message);
    while (rows.hasNext()) {
      Parse row = (Parse) rows.next();
      wrong(row.parts);
      row.parts.addToBody(annotation);
    }
  }

  protected Parse buildRows(Object[] rows) {
    Parse root = new Parse(null, null, null, null);
    Parse next = root;
    for (Object row : rows) {
      next = next.more = new Parse("tr", null, buildCells(row), null);
    }
    return root.more;
  }

  protected Parse buildCells(Object row) {
    if (row == null) {
      Parse nil = new Parse("td", "null", null, null);
      nil.addToTag(" colspan=" + columnBindings.length);
      return nil;
    }
    Parse root = new Parse(null, null, null, null);
    Parse next = root;
    for (Binding columnBinding : columnBindings) {
      next = next.more = new Parse("td", " ", null, null);
      TypeAdapter a = columnBinding.adapter;
      if (a == null) {
        ignore(next);
      } else {
        try {
          a.target = row;
          next.body = gray(escape(a.toString(a.get())));
        }
        catch (Exception e) {
          exception(next, e);
        }
      }
    }
    return root.more;
  }
}