// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved.
// Released under the terms of the CPL Common Public License version 1.0.
package fitnesse.slimTables;
import fitnesse.responders.run.slimResponder.SlimTestContext;
import fitnesse.responders.run.slimResponder.SlimTestSystem;
import fitnesse.slim.converters.BooleanConverter;
import fitnesse.slim.converters.VoidConverter;
import fitnesse.wikitext.Utils;
import java.util.*;
import java.util.regex.Matcher;
public class ScriptTable extends SlimTable {
private static final String SEQUENTIAL_ARGUMENT_PROCESSING_SUFFIX = ";";
private Matcher symbolAssignmentMatcher;
public ScriptTable(Table table, String tableId, SlimTestContext context) {
super(table, tableId, context);
}
protected String getTableType() {
return "scriptTable";
}
public void appendInstructions() {
int rows = table.getRowCount();
if (isScript() && table.getColumnCountInRow(0) > 1)
startActor(0);
for (int row = 1; row < rows; row++)
appendInstructionForRow(row);
}
private boolean isScript() {
return "script".equalsIgnoreCase(table.getCellContents(0, 0));
}
private void appendInstructionForRow(int row) {
String firstCell = table.getCellContents(0, row).trim();
if (firstCell.equalsIgnoreCase("start"))
startActor(row);
else if (firstCell.equalsIgnoreCase("check"))
checkAction(row);
else if (firstCell.equalsIgnoreCase("check not"))
checkNotAction(row);
else if (firstCell.equalsIgnoreCase("reject"))
reject(row);
else if (firstCell.equalsIgnoreCase("ensure"))
ensure(row);
else if (firstCell.equalsIgnoreCase("show"))
show(row);
else if (firstCell.equalsIgnoreCase("note"))
note(row);
else if (isSymbolAssignment(firstCell))
actionAndAssign(row);
else if (firstCell.length() == 0)
note(row);
else if (firstCell.trim().startsWith("#") || firstCell.trim().startsWith("*"))
note(row);
else
action(row);
}
private void actionAndAssign(int row) {
int lastCol = table.getColumnCountInRow(row) - 1;
String symbolName = symbolAssignmentMatcher.group(1);
addExpectation(new SymbolAssignmentExpectation(symbolName, getInstructionTag(), 0, row));
String actionName = getActionNameStartingAt(1, lastCol, row);
if (!actionName.equals("")) {
String[] args = getArgumentsStartingAt(1 + 1, lastCol, row);
callAndAssign(symbolName, "scriptTableActor", actionName, args);
}
}
private boolean isSymbolAssignment(String firstCell) {
symbolAssignmentMatcher = symbolAssignmentPattern.matcher(firstCell);
return symbolAssignmentMatcher.matches();
}
private void action(int row) {
int lastCol = table.getColumnCountInRow(row) - 1;
String actionName = getActionNameStartingAt(0, lastCol, row);
String[] args = getArgumentsStartingAt(0 + 1, lastCol, row);
ScenarioTable scenario = getTestContext().getScenario(Disgracer.disgraceClassName(actionName));
if (scenario != null) {
scenario.call(args, this, row);
} else if (!invokeParameterizedScenarioIfPossible(row)) {
addExpectation(new ScriptActionExpectation(getInstructionTag(), 0, row));
callFunction("scriptTableActor", actionName, (Object[]) args);
}
}
private boolean invokeParameterizedScenarioIfPossible(int row) {
if (table.getColumnCountInRow(row) == 1){
String firstNameCell = table.getCellContents(0, row);
for (ScenarioTable scenario : getScenariosWithMostArgumentsFirst()) {
String[] arguments = scenario.matchParameters(firstNameCell);
if (arguments != null) {
scenario.call(arguments, this, row);
return true;
}
}
}
return false;
}
private List<ScenarioTable> getScenariosWithMostArgumentsFirst() {
Map<String, ScenarioTable> scenarioMap = getTestContext().getScenarios();
List<ScenarioTable> scenarios = new ArrayList<ScenarioTable>(scenarioMap.values());
Collections.sort(scenarios, new ScenarioTableLengthComparator());
return scenarios;
}
private static class ScenarioTableLengthComparator implements java.util.Comparator<ScenarioTable> {
public int compare(ScenarioTable st1, ScenarioTable st2) {
int size1 = st1.getInputs().size();
int size2 = st2.getInputs().size();
return size2 - size1;
}
}
private void note(int row) {
}
private void show(int row) {
int lastCol = table.getColumnCountInRow(row) - 1;
addExpectation(new ShowActionExpectation(getInstructionTag(), 0, row));
invokeAction(1, lastCol, row);
}
private void ensure(int row) {
addExpectation(new EnsureActionExpectation(getInstructionTag(), 0, row));
int lastCol = table.getColumnCountInRow(row) - 1;
invokeAction(1, lastCol, row);
}
private void reject(int row) {
addExpectation(new RejectActionExpectation(getInstructionTag(), 0, row));
int lastCol = table.getColumnCountInRow(row) - 1;
invokeAction(1, lastCol, row);
}
private void checkAction(int row) {
int lastColInAction = table.getColumnCountInRow(row) - 1;
table.getCellContents(lastColInAction, row);
addExpectation(new ReturnedValueExpectation(getInstructionTag(), lastColInAction, row));
invokeAction(1, lastColInAction - 1, row);
}
private void checkNotAction(int row) {
int lastColInAction = table.getColumnCountInRow(row) - 1;
table.getCellContents(lastColInAction, row);
addExpectation(new RejectedValueExpectation(getInstructionTag(), lastColInAction, row));
invokeAction(1, lastColInAction - 1, row);
}
private void invokeAction(int startingCol, int endingCol, int row) {
String actionName = getActionNameStartingAt(startingCol, endingCol, row);
String[] args = getArgumentsStartingAt(startingCol + 1, endingCol, row);
callFunction("scriptTableActor", actionName, (Object[]) args);
}
private String getActionNameStartingAt(int startingCol, int endingCol, int row) {
StringBuffer actionName = new StringBuffer();
actionName.append(table.getCellContents(startingCol, row));
int actionNameCol = startingCol + 2;
while (actionNameCol <= endingCol &&
!invokesSequentialArgumentProcessing(actionName.toString())) {
actionName.append(" ").append(table.getCellContents(actionNameCol, row));
actionNameCol += 2;
}
return actionName.toString().trim();
}
private String[] getArgumentsStartingAt(int startingCol, int endingCol, int row) {
ArgumentExtractor extractor = new ArgumentExtractor(startingCol, endingCol, row);
while (extractor.hasMoreToExtract()) {
addExpectation(new ArgumentExpectation(getInstructionTag(), extractor.argumentColumn, row));
extractor.extractNextArgument();
}
return extractor.getArguments();
}
private boolean invokesSequentialArgumentProcessing(String cellContents) {
return cellContents.endsWith(SEQUENTIAL_ARGUMENT_PROCESSING_SUFFIX);
}
private void startActor(int row) {
int classNameColumn = 1;
String cellContents = table.getCellContents(classNameColumn, row);
String className = Disgracer.disgraceClassName(cellContents);
constructInstance("scriptTableActor", className, classNameColumn, row);
}
public void evaluateReturnValues(Map<String, Object> returnValues) throws Exception {
}
class ArgumentExtractor {
private int argumentColumn;
private int endingCol;
private int row;
private List<String> arguments = new ArrayList<String>();
private int increment = 2;
private boolean sequentialArguments = false;
ArgumentExtractor(int startingCol, int endingCol, int row) {
this.argumentColumn = startingCol;
this.endingCol = endingCol;
this.row = row;
}
public boolean hasMoreToExtract() {
return argumentColumn <= endingCol;
}
public void extractNextArgument() {
arguments.add(table.getUnescapedCellContents(argumentColumn, row));
String argumentKeyword = table.getCellContents(argumentColumn - 1, row);
boolean argumentIsSequential = invokesSequentialArgumentProcessing(argumentKeyword);
sequentialArguments = (sequentialArguments || argumentIsSequential);
increment = sequentialArguments ? 1 : 2;
argumentColumn += increment;
}
public String[] getArguments() {
return arguments.toArray(new String[arguments.size()]);
}
}
private class ScriptActionExpectation extends Expectation {
private ScriptActionExpectation(String instructionTag, int col, int row) {
super(instructionTag, col, row);
}
protected String createEvaluationMessage(String actual, String expected) {
if (actual == null)
return failMessage(expected, "Returned null value.");
else if (actual.equals(VoidConverter.VOID_TAG) || actual.equals("null"))
return expected;
else if (actual.equals(BooleanConverter.FALSE))
return fail(expected);
else if (actual.equals(BooleanConverter.TRUE))
return pass(expected);
else
return expected;
}
}
private class EnsureActionExpectation extends Expectation {
public EnsureActionExpectation(String instructionTag, int col, int row) {
super(instructionTag, col, row);
}
protected String createEvaluationMessage(String actual, String expected) {
return (actual != null && actual.equals(BooleanConverter.TRUE)) ?
pass(expected) : fail(expected);
}
}
private class RejectActionExpectation extends Expectation {
public RejectActionExpectation(String instructionTag, int col, int row) {
super(instructionTag, col, row);
}
protected String createEvaluationMessage(String actual, String expected) {
if (actual == null)
return pass(expected);
else
return actual.equals(BooleanConverter.FALSE) ? pass(expected) : fail(expected);
}
}
private class ShowActionExpectation extends Expectation {
public ShowActionExpectation(String instructionTag, int col, int row) {
super(instructionTag, col, row);
}
protected String createEvaluationMessage(String actual, String expected) {
try {
table.appendCellToRow(getRow(), Utils.escapeHTML(actual));
} catch (Throwable e) {
return failMessage(actual, SlimTestSystem.exceptionToString(e));
}
return expected;
}
}
private class ArgumentExpectation extends Expectation {
private ArgumentExpectation(String instructionTag, int col, int row) {
super(instructionTag, col, row);
}
public void evaluateExpectation(Map<String, Object> returnValues) {
String originalContent = table.getCellContents(getCol(), getRow());
table.setCell(getCol(), getRow(), replaceSymbolsWithFullExpansion(originalContent));
}
protected String createEvaluationMessage(String actual, String expected) {
return null;
}
}
}