You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2010/11/15 13:10:24 UTC

svn commit: r1035242 [2/2] - in /incubator/isis/trunk/viewer/bdd: ./ common/src/main/java/org/apache/isis/viewer/bdd/common/components/ common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/ common/src/main/java/org/apache/isis/viewer/bdd/com...

Copied: incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/SetUpObjectsForFitNesse.java (from r1034020, incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/SetUpObjectsForFitNesse.java)
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/SetUpObjectsForFitNesse.java?p2=incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/SetUpObjectsForFitNesse.java&p1=incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/SetUpObjectsForFitNesse.java&r1=1034020&r2=1035242&rev=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/SetUpObjectsForFitNesse.java (original)
+++ incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/SetUpObjectsForFitNesse.java Mon Nov 15 12:10:23 2010
@@ -1,46 +1,44 @@
-package org.apache.isis.extensions.bdd.fitnesse.internal.fixtures;
+package net.sf.isiscontrib.bdd.fitnesse.internal.fixtures;
 
-import org.apache.isis.extensions.bdd.common.AliasRegistry;
-import org.apache.isis.extensions.bdd.common.CellBinding;
-import org.apache.isis.extensions.bdd.common.Constants;
-import org.apache.isis.extensions.bdd.common.fixtures.SetUpObjectsPeer;
-import org.apache.isis.extensions.bdd.common.fixtures.SetUpObjectsPeer.AssociationVisitor;
-import org.apache.isis.extensions.bdd.common.fixtures.SetUpObjectsPeer.PropertyResult;
-import org.apache.isis.extensions.bdd.common.fixtures.SetUpObjectsPeer.SetUpObjectResult;
-import org.apache.isis.extensions.bdd.common.util.Strings;
-import org.apache.isis.extensions.bdd.fitnesse.internal.AbstractFixture;
-import org.apache.isis.extensions.bdd.fitnesse.internal.CellBindingForFitNesse;
-import org.apache.isis.extensions.bdd.fitnesse.internal.util.FitnesseUtil;
-import org.apache.isis.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.metamodel.spec.feature.OneToOneAssociation;
+import net.sf.isiscontrib.bdd.fitnesse.internal.AbstractFixture;
+import net.sf.isiscontrib.bdd.fitnesse.internal.CellBindingForFitNesse;
+import net.sf.isiscontrib.bdd.fitnesse.internal.util.FitnesseUtil;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.viewer.bdd.common.AliasRegistry;
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.Constants;
+import org.apache.isis.viewer.bdd.common.fixtures.SetUpObjectsPeer;
+import org.apache.isis.viewer.bdd.common.fixtures.SetUpObjectsPeer.AssociationVisitor;
+import org.apache.isis.viewer.bdd.common.fixtures.SetUpObjectsPeer.PropertyResult;
+import org.apache.isis.viewer.bdd.common.fixtures.SetUpObjectsPeer.SetUpObjectResult;
+import org.apache.isis.viewer.bdd.common.util.Strings;
 
 import fit.Fixture;
 import fit.Parse;
 
 public class SetUpObjectsForFitNesse extends AbstractFixture<SetUpObjectsPeer> {
 
-
-    public SetUpObjectsForFitNesse(final AliasRegistry aliasesRegistry,
-            final String className, final SetUpObjectsPeer.Mode mode) {
-        this(aliasesRegistry, className, mode, CellBindingForFitNesse.builder(
-                Constants.ALIAS_RESULT_NAME, Constants.ALIAS_RESULT_HEAD_SET)
-                .autoCreate().build());
+    public SetUpObjectsForFitNesse(final AliasRegistry aliasesRegistry, final String className,
+        final SetUpObjectsPeer.Mode mode) {
+        this(aliasesRegistry, className, mode, CellBindingForFitNesse
+            .builder(Constants.ALIAS_RESULT_NAME, Constants.ALIAS_RESULT_HEAD_SET).autoCreate().build());
     }
 
-    private SetUpObjectsForFitNesse(final AliasRegistry aliasesRegistry,
-            final String className, final SetUpObjectsPeer.Mode mode,
-            final CellBinding aliasBinding) {
+    private SetUpObjectsForFitNesse(final AliasRegistry aliasesRegistry, final String className,
+        final SetUpObjectsPeer.Mode mode, final CellBinding aliasBinding) {
         super(new SetUpObjectsPeer(aliasesRegistry, className, mode, aliasBinding));
     }
 
     @Override
     public void doTable(final Parse table) {
 
-    	final Parse classNameCell = table.parts.parts.more;
-    	if (getPeer().isSpecOk()) {
+        final Parse classNameCell = table.parts.parts.more;
+        if (getPeer().isSpecOk()) {
             FitnesseUtil.exception(this, classNameCell, "(no such class)");
             return;
-    	}
+        }
 
         super.doTable(table);
 
@@ -52,47 +50,45 @@ public class SetUpObjectsForFitNesse ext
         }
     }
 
-	@Override
+    @Override
     protected void doRowsWithBindings(final Parse headRow) {
         Parse eachHead = headRow.parts;
         try {
-        	eachHead = addProperties(eachHead);
+            eachHead = addProperties(eachHead);
         } catch (final Throwable throwable) {
             exception(eachHead, throwable);
         }
     }
 
-	private Parse addProperties(Parse eachHead) {
-		for (int i = 0; eachHead != null; i++, eachHead = eachHead.more) {
-		    final String headText = eachHead.text();
-
-		    PropertyResult status = addProperty(headText, i);
-		    if (!status.isOk()) {
-		    	if (status == PropertyResult.NO_SUCH_PROPERTY) {
-		    		FitnesseUtil
-		    		.exception(this, eachHead, "(no such property)");
-		    	} else 
-		    	if (status == PropertyResult.NOT_A_PROPERTY) {
-		    		FitnesseUtil.exception(this, eachHead, "(not a property)");
-		    	}
-		    }
-		}
-		return eachHead;
-	}
-
-	private PropertyResult addProperty(final String heading, int colNum) {
-		return getPeer().definePropertyOrAlias(heading, colNum);
-	}
+    private Parse addProperties(Parse eachHead) {
+        for (int i = 0; eachHead != null; i++, eachHead = eachHead.more) {
+            final String headText = eachHead.text();
+
+            PropertyResult status = addProperty(headText, i);
+            if (!status.isOk()) {
+                if (status == PropertyResult.NO_SUCH_PROPERTY) {
+                    FitnesseUtil.exception(this, eachHead, "(no such property)");
+                } else if (status == PropertyResult.NOT_A_PROPERTY) {
+                    FitnesseUtil.exception(this, eachHead, "(not a property)");
+                }
+            }
+        }
+        return eachHead;
+    }
+
+    private PropertyResult addProperty(final String heading, int colNum) {
+        return getPeer().definePropertyOrAlias(heading, colNum);
+    }
 
     @Override
     public void doRow(final Parse row) {
 
         // copied down from superclass
         clearCellTextList();
-        
+
         doCells(row.parts);
         if (!getPeer().getAliasBinding().isFound()) {
-			addCellText(""); // placeholder
+            addCellText(""); // placeholder
         }
 
         try {
@@ -104,58 +100,58 @@ public class SetUpObjectsForFitNesse ext
         }
     }
 
-	private void clearCellTextList() {
-		getPeer().resetForNextObject();
-	}
-
-	private boolean addCellText(String cellText) {
-		return getPeer().addPropertyValueOrAlias(cellText);
-	}
+    private void clearCellTextList() {
+        getPeer().resetForNextObject();
+    }
+
+    private boolean addCellText(String cellText) {
+        return getPeer().addPropertyValueOrAlias(cellText);
+    }
 
     @Override
     public void doCell(final Parse cell, final int column) {
         super.doCell(cell, column);
         String cellText = cell.text();
-		addCellText(cellText);
+        addCellText(cellText);
     }
 
     private void setUpObject(final Parse row) throws Exception {
 
-    	// spec for adapter should have been initialized in doTable.
+        // spec for adapter should have been initialized in doTable.
         final ObjectAdapter adapter = getPeer().createInstance();
         if (adapter == null) {
-        	return;
+            return;
         }
 
         // TODO: redo using createObject(), catch the StoryFailureException instead
         for (int colNum = 0; colNum < getPeer().getProperties().size(); colNum++) {
-        	
-        	SetUpObjectResult result = getPeer().setUpProperty(adapter, colNum);
-            
+
+            SetUpObjectResult result = getPeer().setUpProperty(adapter, colNum);
+
             if (!result.isHandled()) {
-            	final Parse cell = FitnesseUtil.cell(row, colNum);
-            	if (result.isError()) {
-            		FitnesseUtil.exception(this, cell, result.getErrorMessage());
-            	} else {
-            		right(cell);
-            	}
+                final Parse cell = FitnesseUtil.cell(row, colNum);
+                if (result.isError()) {
+                    FitnesseUtil.exception(this, cell, result.getErrorMessage());
+                } else {
+                    right(cell);
+                }
             }
         }
 
         getPeer().persistIfNecessary(adapter);
 
         if (getPeer().getAliasBinding().isFound()) {
-        	// TODO: review - this block may be redundant since the peer now captures
-        	// the alias when the text is added (earlier).
+            // TODO: review - this block may be redundant since the peer now captures
+            // the alias when the text is added (earlier).
             int aliasColumn = getPeer().getAliasBinding().getColumn();
-			final Parse aliasCell = FitnesseUtil.cell(row, aliasColumn);
+            final Parse aliasCell = FitnesseUtil.cell(row, aliasColumn);
             if (aliasCell != null) {
                 String alias = aliasCell.text();
                 if (Strings.emptyString(alias)) {
                     alias = getPeer().aliasFor(adapter);
                     FitnesseUtil.setBody(aliasCell, Fixture.gray(alias));
                 } else {
-                	getPeer().getAliasRegistry().aliasAs(alias, adapter);
+                    getPeer().getAliasRegistry().aliasAs(alias, adapter);
                 }
             }
         } else {
@@ -164,26 +160,28 @@ public class SetUpObjectsForFitNesse ext
         }
     }
 
-	private void rightAllKnownAssociationCells(final Parse row) {
-		getPeer().forEachAssociation(new AssociationVisitor() {
-			public void visit(OneToOneAssociation association, int i) {
-				final Parse cell = FitnesseUtil.cell(row, i);
-				if (cell != null) {
-					right(cell);
-				}
-			}
-		});
+    private void rightAllKnownAssociationCells(final Parse row) {
+        getPeer().forEachAssociation(new AssociationVisitor() {
+            @Override
+            public void visit(OneToOneAssociation association, int i) {
+                final Parse cell = FitnesseUtil.cell(row, i);
+                if (cell != null) {
+                    right(cell);
+                }
+            }
+        });
     }
 
     private void wrongAllKnownAssociationCells(final Parse row) {
-    	getPeer().forEachAssociation(new AssociationVisitor() {
-			public void visit(OneToOneAssociation association, int i) {
-				final Parse cell = FitnesseUtil.cell(row, i);
-				if (cell != null) {
-					wrong(cell);
-				}
-			}
-		});
+        getPeer().forEachAssociation(new AssociationVisitor() {
+            @Override
+            public void visit(OneToOneAssociation association, int i) {
+                final Parse cell = FitnesseUtil.cell(row, i);
+                if (cell != null) {
+                    wrong(cell);
+                }
+            }
+        });
     }
 
     private Parse appendCell(final Parse row, final String text) {

Copied: incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/UsingIsisViewerForFitNesse.java (from r1034020, incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/UsingNakedObjectsViewerForFitNesse.java)
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/UsingIsisViewerForFitNesse.java?p2=incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/UsingIsisViewerForFitNesse.java&p1=incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/UsingNakedObjectsViewerForFitNesse.java&r1=1034020&r2=1035242&rev=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/UsingNakedObjectsViewerForFitNesse.java (original)
+++ incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/UsingIsisViewerForFitNesse.java Mon Nov 15 12:10:23 2010
@@ -1,167 +1,145 @@
-package org.apache.isis.extensions.bdd.fitnesse.internal.fixtures;
+package net.sf.isiscontrib.bdd.fitnesse.internal.fixtures;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.isis.extensions.bdd.common.AliasRegistry;
-import org.apache.isis.extensions.bdd.common.CellBinding;
-import org.apache.isis.extensions.bdd.common.Constants;
-import org.apache.isis.extensions.bdd.common.StoryBoundValueException;
-import org.apache.isis.extensions.bdd.common.StoryCell;
-import org.apache.isis.extensions.bdd.common.fixtures.UsingNakedObjectsViewerPeer;
-import org.apache.isis.extensions.bdd.common.fixtures.perform.Perform;
-import org.apache.isis.extensions.bdd.fitnesse.internal.AbstractFixture;
-import org.apache.isis.extensions.bdd.fitnesse.internal.CellBindingForFitNesse;
-import org.apache.isis.extensions.bdd.fitnesse.internal.fixtures.perform.StoryCellForFitNesse;
-import org.apache.isis.extensions.bdd.fitnesse.internal.util.FitnesseUtil;
-import org.apache.isis.metamodel.adapter.ObjectAdapter;
-import org.apache.isis.metamodel.spec.feature.ObjectMember;
+import net.sf.isiscontrib.bdd.fitnesse.internal.AbstractFixture;
+import net.sf.isiscontrib.bdd.fitnesse.internal.CellBindingForFitNesse;
+import net.sf.isiscontrib.bdd.fitnesse.internal.fixtures.perform.StoryCellForFitNesse;
+import net.sf.isiscontrib.bdd.fitnesse.internal.util.FitnesseUtil;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.viewer.bdd.common.AliasRegistry;
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.Constants;
+import org.apache.isis.viewer.bdd.common.StoryBoundValueException;
+import org.apache.isis.viewer.bdd.common.StoryCell;
+import org.apache.isis.viewer.bdd.common.fixtures.UsingIsisViewerPeer;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.Perform;
 
 import com.google.common.collect.Lists;
 
 import fit.Parse;
 import fit.exception.FitFailureException;
 
-public class UsingNakedObjectsViewerForFitNesse extends
-		AbstractFixture<UsingNakedObjectsViewerPeer> {
+public class UsingIsisViewerForFitNesse extends AbstractFixture<UsingIsisViewerPeer> {
 
-	private final List<Parse> argumentCells = new ArrayList<Parse>();
-
-	public UsingNakedObjectsViewerForFitNesse(
-			final AliasRegistry aliasesRegistry,
-			final Perform.Mode mode) {
-		this(aliasesRegistry, mode, 
-				CellBindingForFitNesse.builder(
-						Constants.ON_OBJECT_NAME, Constants.ON_OBJECT_HEAD_SET)
-						.ditto().build(),
-				CellBindingForFitNesse.builder(
-						Constants.ALIAS_RESULT_NAME, Constants.ALIAS_RESULT_HEAD_SET)
-						.optional().build(), 
-				CellBindingForFitNesse.builder(
-						Constants.PERFORM_NAME, Constants.PERFORM_HEAD_SET)
-						.ditto().build(), 
-				CellBindingForFitNesse.builder(
-						Constants.ON_MEMBER_NAME, Constants.ON_MEMBER_HEAD_SET)
-						.optional().build(),
-				CellBindingForFitNesse.builder(
-						Constants.THAT_IT_NAME, Constants.THAT_IT_HEAD_SET)
-						.ditto().optional().build(), 
-				CellBindingForFitNesse.builder(
-						Constants.WITH_ARGUMENTS_NAME, Constants.WITH_ARGUMENTS_HEAD_SET)
-						.optional().build());
-	}
-
-	private UsingNakedObjectsViewerForFitNesse(
-			final AliasRegistry aliasesRegistry,
-			final Perform.Mode mode,
-			final CellBinding onObjectBinding,
-			final CellBinding aliasResultAsBinding,
-			final CellBinding performBinding,
-			final CellBinding onMemberBinding, final CellBinding thatItBinding,
-			final CellBinding arg0Binding) {
-		super(new UsingNakedObjectsViewerPeer(aliasesRegistry, mode,
-				onObjectBinding, aliasResultAsBinding, performBinding,
-				onMemberBinding, thatItBinding, arg0Binding));
-	}
-
-	@Override
-	public void doRows(final Parse headRow) {
-		super.doRows(headRow);
-		ensureArgBindingLast();
-	}
-
-	private void ensureArgBindingLast() {
-		if (getPeer().isArg0BindingLast()) {
-			return;
-		}
-		FitnesseUtil.exception(this, getPeer().getArg0Binding().getHeadCell(),
-				"(must be last column)");
-		throw new FitFailureException("(invalid binding order)");
-	}
-
-	@Override
-	public void doRow(final Parse row) {
-		argumentCells.clear();
-		super.doRow(row);
-		execute();
-	}
-
-	@Override
-	public void doCell(final Parse cell, final int columnNum) {
-		super.doCell(cell, columnNum);
-
-		captureArgumentCellsIfAny(cell, columnNum);
-	}
-
-	private void captureArgumentCellsIfAny(final Parse cell, final int columnNum) {
-		if (!getPeer().getArg0Binding().isFound()) {
-			return;
-		}
-		if (columnNum < getPeer().getArg0Binding().getColumn()) {
-			return;
-		}
-		argumentCells.add(cell);
-	}
-
-	private void execute() {
-
-		ObjectAdapter onAdapter = null;
-		try {
-			onAdapter = getPeer().validateOnObject();
-		} catch (StoryBoundValueException ex) {
-			FitnesseUtil.exception(this, ex);
-			return;
-		}
-
-		String aliasAs = null;
-		try {
-			aliasAs = getPeer().validateAliasAs();
-		} catch (StoryBoundValueException ex) {
-			FitnesseUtil.exception(this, ex);
-			return;
-		}
-
-		Perform performCommand = null;
-		try {
-			performCommand = getPeer().validatePerform();
-		} catch (StoryBoundValueException ex) {
-			FitnesseUtil.exception(this, ex);
-			return;
-		}
-
-		ObjectMember nakedObjectMember = null;
-		if (performCommand.requiresMember()) {
-			try {
-				nakedObjectMember = getPeer().validateOnMember(onAdapter);
-			} catch (StoryBoundValueException ex) {
-				FitnesseUtil.exception(this, ex);
-				return;
-			}
-		}
-
-		try {
-			List<StoryCell> argumentStoryCells = asValues(argumentCells);
-			getPeer().performCommand(performCommand, onAdapter, nakedObjectMember,
-					argumentStoryCells, aliasAs);
-		} catch (StoryBoundValueException ex) {
-			FitnesseUtil.exception(this, ex);
-			return;
-		}
-
-		rightMemberElseOnObjectCell();
-	}
-
-	private static List<StoryCell> asValues(List<Parse> argCells) {
-		List<StoryCell> storyValues = Lists.newArrayList();
-		for (Parse parse : argCells) {
-			storyValues.add(new StoryCellForFitNesse(parse));
-		}
-		return storyValues;
-	}
-
-	private void rightMemberElseOnObjectCell() {
-		right(getPeer().getMemberElseOnObjectCell());
-	}
+    private final List<Parse> argumentCells = new ArrayList<Parse>();
 
+    public UsingIsisViewerForFitNesse(final AliasRegistry aliasesRegistry, final Perform.Mode mode) {
+        this(aliasesRegistry, mode, CellBindingForFitNesse
+            .builder(Constants.ON_OBJECT_NAME, Constants.ON_OBJECT_HEAD_SET).ditto().build(), CellBindingForFitNesse
+            .builder(Constants.ALIAS_RESULT_NAME, Constants.ALIAS_RESULT_HEAD_SET).optional().build(),
+            CellBindingForFitNesse.builder(Constants.PERFORM_NAME, Constants.PERFORM_HEAD_SET).ditto().build(),
+            CellBindingForFitNesse.builder(Constants.ON_MEMBER_NAME, Constants.ON_MEMBER_HEAD_SET).optional().build(),
+            CellBindingForFitNesse.builder(Constants.THAT_IT_NAME, Constants.THAT_IT_HEAD_SET).ditto().optional()
+                .build(), CellBindingForFitNesse
+                .builder(Constants.WITH_ARGUMENTS_NAME, Constants.WITH_ARGUMENTS_HEAD_SET).optional().build());
+    }
+
+    private UsingIsisViewerForFitNesse(final AliasRegistry aliasesRegistry, final Perform.Mode mode,
+        final CellBinding onObjectBinding, final CellBinding aliasResultAsBinding, final CellBinding performBinding,
+        final CellBinding onMemberBinding, final CellBinding thatItBinding, final CellBinding arg0Binding) {
+        super(new UsingIsisViewerPeer(aliasesRegistry, mode, onObjectBinding, aliasResultAsBinding, performBinding,
+            onMemberBinding, thatItBinding, arg0Binding));
+    }
+
+    @Override
+    public void doRows(final Parse headRow) {
+        super.doRows(headRow);
+        ensureArgBindingLast();
+    }
+
+    private void ensureArgBindingLast() {
+        if (getPeer().isArg0BindingLast()) {
+            return;
+        }
+        FitnesseUtil.exception(this, getPeer().getArg0Binding().getHeadCell(), "(must be last column)");
+        throw new FitFailureException("(invalid binding order)");
+    }
+
+    @Override
+    public void doRow(final Parse row) {
+        argumentCells.clear();
+        super.doRow(row);
+        execute();
+    }
+
+    @Override
+    public void doCell(final Parse cell, final int columnNum) {
+        super.doCell(cell, columnNum);
+
+        captureArgumentCellsIfAny(cell, columnNum);
+    }
+
+    private void captureArgumentCellsIfAny(final Parse cell, final int columnNum) {
+        if (!getPeer().getArg0Binding().isFound()) {
+            return;
+        }
+        if (columnNum < getPeer().getArg0Binding().getColumn()) {
+            return;
+        }
+        argumentCells.add(cell);
+    }
+
+    private void execute() {
+
+        ObjectAdapter onAdapter = null;
+        try {
+            onAdapter = getPeer().validateOnObject();
+        } catch (StoryBoundValueException ex) {
+            FitnesseUtil.exception(this, ex);
+            return;
+        }
+
+        String aliasAs = null;
+        try {
+            aliasAs = getPeer().validateAliasAs();
+        } catch (StoryBoundValueException ex) {
+            FitnesseUtil.exception(this, ex);
+            return;
+        }
+
+        Perform performCommand = null;
+        try {
+            performCommand = getPeer().validatePerform();
+        } catch (StoryBoundValueException ex) {
+            FitnesseUtil.exception(this, ex);
+            return;
+        }
+
+        ObjectMember nakedObjectMember = null;
+        if (performCommand.requiresMember()) {
+            try {
+                nakedObjectMember = getPeer().validateOnMember(onAdapter);
+            } catch (StoryBoundValueException ex) {
+                FitnesseUtil.exception(this, ex);
+                return;
+            }
+        }
+
+        try {
+            List<StoryCell> argumentStoryCells = asValues(argumentCells);
+            getPeer().performCommand(performCommand, onAdapter, nakedObjectMember, argumentStoryCells, aliasAs);
+        } catch (StoryBoundValueException ex) {
+            FitnesseUtil.exception(this, ex);
+            return;
+        }
+
+        rightMemberElseOnObjectCell();
+    }
+
+    private static List<StoryCell> asValues(List<Parse> argCells) {
+        List<StoryCell> storyValues = Lists.newArrayList();
+        for (Parse parse : argCells) {
+            storyValues.add(new StoryCellForFitNesse(parse));
+        }
+        return storyValues;
+    }
+
+    private void rightMemberElseOnObjectCell() {
+        right(getPeer().getMemberElseOnObjectCell());
+    }
 
 }

Copied: incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/perform/StoryCellForFitNesse.java (from r1034020, incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/perform/StoryCellForFitNesse.java)
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/perform/StoryCellForFitNesse.java?p2=incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/perform/StoryCellForFitNesse.java&p1=incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/perform/StoryCellForFitNesse.java&r1=1034020&r2=1035242&rev=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/org/apache/isis/extensions/bdd/fitnesse/internal/fixtures/perform/StoryCellForFitNesse.java (original)
+++ incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/fixtures/perform/StoryCellForFitNesse.java Mon Nov 15 12:10:23 2010
@@ -1,33 +1,36 @@
-package org.apache.isis.extensions.bdd.fitnesse.internal.fixtures.perform;
+package net.sf.isiscontrib.bdd.fitnesse.internal.fixtures.perform;
 
-import org.apache.isis.extensions.bdd.common.StoryCell;
+import org.apache.isis.viewer.bdd.common.StoryCell;
 
 import fit.Parse;
 
 public class StoryCellForFitNesse implements StoryCell {
-	
-	private Parse source;
 
-	public StoryCellForFitNesse(Parse source) {
-		this.source = source;
-	}
-
-	public String getText() {
-		return source.text();
-	}
-
-	/**
-	 * The implementation-specific representation of this text.
-	 * 
-	 * <p>
-	 * Holds a Fit {@link Parse} object.
-	 */
-	public Object getSource() {
-		return source;
-	}
-
-	public void setText(String str) {
-		source.body = str;
-	}
-	
+    private final Parse source;
+
+    public StoryCellForFitNesse(Parse source) {
+        this.source = source;
+    }
+
+    @Override
+    public String getText() {
+        return source.text();
+    }
+
+    /**
+     * The implementation-specific representation of this text.
+     * 
+     * <p>
+     * Holds a Fit {@link Parse} object.
+     */
+    @Override
+    public Object getSource() {
+        return source;
+    }
+
+    @Override
+    public void setText(String str) {
+        source.body = str;
+    }
+
 }

Modified: incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/Aliases.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/Aliases.java?rev=1035242&r1=1034020&r2=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/Aliases.java (original)
+++ incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/Aliases.java Mon Nov 15 12:10:23 2010
@@ -1,18 +1,19 @@
-package org.apache.isis.extensions.bdd.fitnesse.internal.util;
+package net.sf.isiscontrib.bdd.fitnesse.internal.util;
 
-import org.apache.isis.extensions.bdd.common.CellBinding;
-import org.apache.isis.extensions.bdd.common.Constants;
-import org.apache.isis.extensions.bdd.fitnesse.internal.fixtures.perform.StoryCellForFitNesse;
+import net.sf.isiscontrib.bdd.fitnesse.internal.fixtures.perform.StoryCellForFitNesse;
+
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.Constants;
 
 import fit.Fixture;
 import fit.Parse;
 
 public final class Aliases {
 
-    private Aliases() {}
+    private Aliases() {
+    }
 
-    public static CellBinding findOrCreateAlias(final Parse heads,
-            final CellBinding aliasInfo) {
+    public static CellBinding findOrCreateAlias(final Parse heads, final CellBinding aliasInfo) {
         Parse eachHead = heads;
         for (int i = 0; eachHead != null; i++, eachHead = eachHead.more) {
             final String headText = eachHead.body;
@@ -26,8 +27,7 @@ public final class Aliases {
         // Append an alias cell to header
         if (!aliasInfo.isFound()) {
             final int size = heads.size();
-            final Parse aliasCell = new Parse("td", Fixture
-                    .gray(Constants.ALIAS_RESULT_HEAD), null, null);
+            final Parse aliasCell = new Parse("td", Fixture.gray(Constants.ALIAS_RESULT_HEAD), null, null);
             heads.last().more = aliasCell;
             aliasInfo.create(size, new StoryCellForFitNesse(aliasCell));
         }
@@ -35,8 +35,7 @@ public final class Aliases {
     }
 
     public static boolean hasAliasText(final String headText) {
-        return Constants.ALIAS_RESULT_HEAD_ALT1.equals(headText)
-                || Constants.ALIAS_RESULT_HEAD.equals(headText);
+        return Constants.ALIAS_RESULT_HEAD_ALT1.equals(headText) || Constants.ALIAS_RESULT_HEAD.equals(headText);
     }
 
 }

Modified: incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/FitnesseUtil.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/FitnesseUtil.java?rev=1035242&r1=1034020&r2=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/FitnesseUtil.java (original)
+++ incubator/isis/trunk/viewer/bdd/fitnesse/src/main/java/net/sf/isiscontrib/bdd/fitnesse/internal/util/FitnesseUtil.java Mon Nov 15 12:10:23 2010
@@ -1,7 +1,7 @@
-package org.apache.isis.extensions.bdd.fitnesse.internal.util;
+package net.sf.isiscontrib.bdd.fitnesse.internal.util;
 
-import org.apache.isis.extensions.bdd.common.StoryBoundValueException;
-import org.apache.isis.extensions.bdd.common.StoryCell;
+import org.apache.isis.viewer.bdd.common.StoryBoundValueException;
+import org.apache.isis.viewer.bdd.common.StoryCell;
 
 import fit.Fixture;
 import fit.Parse;
@@ -9,51 +9,49 @@ import fit.exception.FitFailureException
 
 public final class FitnesseUtil {
 
-	private FitnesseUtil() {
-	}
+    private FitnesseUtil() {
+    }
 
-	public static Parse cell(final Parse row, final int columnNumber) {
-		Parse cell = row.parts;
-		if (cell == null) {
-			return cell;
-		}
-		int i = 0;
-		while (true) {
-			if (columnNumber == i) {
-				return cell;
-			}
-			if (cell.more == null) {
-				return cell;
-			}
-			cell = cell.more;
-			i++;
-		}
-	}
-
-	public static void exception(Fixture fixture, StoryBoundValueException ex) {
-		exception(fixture, ex.getStoryCell(), ex.getMessage());
-	}
-
-	public static void exception(final Fixture fixture,
-			final StoryCell storyCell, final String message) {
-		exception(fixture, asParse(storyCell), message);
-	}
-
-	public static Parse asParse(final StoryCell storyValue) {
-		return (Parse) storyValue.getSource();
-	}
-
-	public static void exception(final Fixture fixture, final Parse cell,
-			final String message) {
-		fixture.exception(cell, new FitFailureException(message));
-	}
-
-	public static void setText(StoryCell storyCell, String str) {
-		storyCell.setText(str);
-	}
-
-	public static void setBody(Parse parse, String str) {
-		parse.body = str;
-	}
+    public static Parse cell(final Parse row, final int columnNumber) {
+        Parse cell = row.parts;
+        if (cell == null) {
+            return cell;
+        }
+        int i = 0;
+        while (true) {
+            if (columnNumber == i) {
+                return cell;
+            }
+            if (cell.more == null) {
+                return cell;
+            }
+            cell = cell.more;
+            i++;
+        }
+    }
+
+    public static void exception(Fixture fixture, StoryBoundValueException ex) {
+        exception(fixture, ex.getStoryCell(), ex.getMessage());
+    }
+
+    public static void exception(final Fixture fixture, final StoryCell storyCell, final String message) {
+        exception(fixture, asParse(storyCell), message);
+    }
+
+    public static Parse asParse(final StoryCell storyValue) {
+        return (Parse) storyValue.getSource();
+    }
+
+    public static void exception(final Fixture fixture, final Parse cell, final String message) {
+        fixture.exception(cell, new FitFailureException(message));
+    }
+
+    public static void setText(StoryCell storyCell, String str) {
+        storyCell.setText(str);
+    }
+
+    public static void setBody(Parse parse, String str) {
+        parse.body = str;
+    }
 
 }

Modified: incubator/isis/trunk/viewer/bdd/fitnesse/src/site/site.xml
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/fitnesse/src/site/site.xml?rev=1035242&r1=1035241&r2=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/fitnesse/src/site/site.xml (original)
+++ incubator/isis/trunk/viewer/bdd/fitnesse/src/site/site.xml Mon Nov 15 12:10:23 2010
@@ -1,26 +1,15 @@
 <project name="${project.name}">
     <version position="right"/>
+    
     <bannerLeft>
         <name>HAL</name>
         <src>images/hal-logo-for-maven-site.jpg</src>
         <href>.</href>
     </bannerLeft>
 
-    <poweredBy>
-        <logo name="Naked Objects" href="http://nakedobjects.org"
-              img="images/NO-powered-by-logo.png"/>
-    </poweredBy>
-
-    <skin>
-        <groupId>org.starobjects.star</groupId>
-        <artifactId>skin</artifactId>
-        <version>1</version>
-    </skin>
-
     <body>
         <links>
-          <item name="Naked Objects" href="http://www.nakedobjects.org/"/>
-          <item name="Sister Projects" href="http://starobjects.sourceforge.net"/>
+          <item name="Apache Isis" href="http://incubator.apache.org/isis/"/>
         </links>
 
         <menu ref="modules" inherit="top"/>
@@ -28,26 +17,17 @@
         <menu name="Documentation">
             <item name="User Guide (PDF)" href="documentation/docbkx/pdf/user-guide.pdf"/>
             <item name="User Guide (HTML)" href="documentation/docbkx/html/user-guide/user-guide.html"/>
-            <item name="Developers Guide (PDF)" href="documentation/docbkx/pdf/developers-guide.pdf"/>
-            <item name="Developers Guide (HTML)" href="documentation/docbkx/html/developers-guide/developers-guide.html"/>
         </menu>
 
         <menu name="Project Resources">
-            <item name="SF Trac Wiki" href="http://sourceforge.net/apps/trac/testedobjects"/>
-            <item name="SF Project Page" href="http://sourceforge.net/projects/testedobjects"/>
-        </menu>
-
-        <menu name="Sister Projects">
-            <item name="Home" href="http://starobjects.sourceforge.net"/>
-            <item name="Maven Repo" href="http://starobjects.sourceforge.net/m2-repo"/>
+            <item name="SF Trac Wiki" href="http://sourceforge.net/apps/trac/isis-contrib"/>
+            <item name="SF Project Page" href="http://sourceforge.net/projects/isis-contrib"/>
+            <item name="Maven Repo" href="http://isis-contrib.sourceforge.net/m2-repo"/>
         </menu>
 
         <menu name="Other Resources">
             <item name="Dan Haywood's blog" href="http://danhaywood.com"/>
-            <item name="Naked Objects" href="http://www.nakedobjects.org"/>
-            <item name="Naked Objects blog" href="http://blog.nakedobjects.org"/>
-            <item name="Naked Objects for .NET" href="http://www.nakedobjects.net"/>
-            <item name="Scimpi" href="http://www.scimpi.org/" />
+            <item name="DDD using Naked Objects" href="http://www.pragprog/com/titles/dhnako" />
         </menu>
 
     </body>

Modified: incubator/isis/trunk/viewer/bdd/pom.xml
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/pom.xml?rev=1035242&r1=1035241&r2=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/pom.xml (original)
+++ incubator/isis/trunk/viewer/bdd/pom.xml Mon Nov 15 12:10:23 2010
@@ -101,9 +101,7 @@
     <modules>
 	    <module>common</module>
 	    <module>concordion</module>
-        <!-- 
 	    <module>fitnesse</module>
-        -->
 	</modules>
 
 </project>

Modified: incubator/isis/trunk/viewer/bdd/src/docbkx/guide/isis-bdd-viewer.xml
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/viewer/bdd/src/docbkx/guide/isis-bdd-viewer.xml?rev=1035242&r1=1035241&r2=1035242&view=diff
==============================================================================
--- incubator/isis/trunk/viewer/bdd/src/docbkx/guide/isis-bdd-viewer.xml (original)
+++ incubator/isis/trunk/viewer/bdd/src/docbkx/guide/isis-bdd-viewer.xml Mon Nov 15 12:10:23 2010
@@ -1,10 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
-"file:./resources/docbook-xml-4.5/docbookx.dtd">
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+"file:./resources/docbook-xml-4.5/docbookx.dtd">
 <book>
   <bookinfo>
     <title><?eval ${docbkxGuideTitle}?></title>
+
     <subtitle><?eval ${docbkxGuideSubTitle}?></subtitle>
+
     <releaseinfo><?eval ${project.version}?></releaseinfo>
 
     <authorgroup>
@@ -29,29 +31,32 @@
   <preface id="preface">
     <title>Preface</title>
 
-    <para><ulink url="http://starobjects.org">Tested Objects</ulink> is a
-    sister project for the <ulink url="http://nakedobjects.org">Naked
-    Objects</ulink> framework, providing integration with <ulink
-    url="http://fitnesse.org">FitNesse</ulink> to enable agile acceptance (or
-    scenario) testing. The integration bundles the FitNesse wiki server and
-    provides a set of generic Fitnesse fixtures that interact with the domain
-    model in the same manner that a Naked Objects viewer does. It also
-    provides a Maven archetype to get you started quickly.</para>
-
-    <para>This user guide describes how to use Tested Objects to test your
-    Naked Objects domain applications through FitNesse. Much of this guidance
-    relates to using the Maven archetype; for ease of reference the archetype
-    creates an online user guide within the FitNesse wiki. If you are
-    interested in building Tested Objects from source code (perhaps with a
-    view to contributing to or extending the capabilities of Tested Objects
-    itself) then please see the developers' guide.</para>
-
-    <para>Tested Objects is hosted on <ulink
-    url="http://testedobjects.sourceforge.net">SourceForge</ulink>, and is
-    licensed under <ulink
+    <para>Behaviour-driven development is a means to drive the development of
+    an application through stories and scenarios. These are expressed in a
+    semi-formal textual form that can be understood (or indeed be written) by
+    the domain expert/business analyst, but which can then be used to directly
+    exercise the system under test as it is developed.</para>
+
+    <para>A number of frameworks exist to streamline this process. Generally
+    these require the developer to write glue code that acts as a bridge from
+    the textual specification and the system under test.</para>
+
+    <para>The <emphasis>BDD Viewer</emphasis> module for <emphasis>Apache
+    Isis</emphasis> aims to allow <acronym>BDD</acronym> stories/scenarios to
+    be written against the domain model of an Isis application, without the
+    developer having to write any glue code. It consists of a common library
+    that abstracts the interaction with the Isis metamodel, along with an
+    integration (that uses this common library) for one particular
+    <acronym>BDD</acronym> framework, namely <ulink
+    url="http://concordion.org">Concordion</ulink>.</para>
+
+    <para>This user guide describes how to use the Concordion integration,
+    along with details of the common library so that other BDD frameworks can
+    be integrated if required.</para>
+
+    <para><emphasis>Apache Isis</emphasis> is licensed under <ulink
     url="http://www.apache.org/licenses/LICENSE-2.0.html">Apache Software
-    License v2</ulink>. Naked Objects is also hosted on SourceForge, and is
-    also licensed under Apache Software License v2.</para>
+    License v2</ulink>.</para>
   </preface>
 
   <!-- main content -->
@@ -60,7 +65,7 @@
     <title>Introduction</title>
 
     <sect1>
-      <title>Scenario Testing (aka Agile Acceptance Testing)</title>
+      <title>Behaviour-driven Development</title>
 
       <para>Prior to agile development, requirements gathering for systems was
       traditionally performed by business analysts discussing requirements
@@ -72,7 +77,7 @@
       out-of-date) requirements documentation and (as often as not)
       reverse-engineering the implementation.</para>
 
-      <para>Scenario testing combines the requirements capture and the
+      <para>Behaviour-driven development combines requirements capture and the
       acceptance test criteria in a single form, through scenarios. As before,
       these requirements are in a form that a non-technical domain expert from
       the business can understand. What differs though is that these scenarios
@@ -88,504 +93,200 @@
       non-technical businesss person. Compare this to unit testing which
       exercises the behaviour / method of a single class.</para>
 
-      <para>Scenario testing is more usually called "agile acceptance
-      testing", but I find that term rather clumsy. Other names also exist,
-      including behaviour-driven development, and example-driven development.
-      Here we use our own term "scenario testing".</para>
-    </sect1>
-
-    <sect1>
-      <title>Introduction to FitNesse</title>
-
-      <para><ulink url="http://fitnesse.org">FitNesse</ulink> is an framework
-      to enable scenario testing. It builds upon the earlier <ulink
-      url="http://fit.c2.com/">FIT</ulink> framework, which executes tests
-      expressed in terms of tables. FitNesse sits on top of FIT<footnote>
-          <para>In fact, FitNesse as of end 2008 has a replacement test
-          execution architecture, called <ulink
-          url="http://fitnesse.org/FitNesse.UserGuide.SliM">SLIM</ulink>.</para>
-        </footnote>, allowing the tables to be written within a Wiki, and
-      spawning off FIT to run the tests. The results of the tests are shown as
-      annotated tests, as shown below.</para>
-
-      <screenshot>
-        <screeninfo>The ClaimsAppSuite test results</screeninfo>
-
-        <mediaobject>
-          <imageobject>
-            <imagedata fileref="images/ClaimsAppSuite-TestResults.png"
-                       scale="37" />
-          </imageobject>
-        </mediaobject>
-      </screenshot>
-
-      <para>This makes it easy for non-technical business users to both author
-      new tests, and to view their execution. It also creates an efficient
-      feedback loop; a FitNesse test will "keep on going" even if it hits a
-      failure. Thus the developer can identify several issues and fix them in
-      a single pass.</para>
-
-      <para>Another way to think of FitNesse is as a replacement presentation
-      layer, hitting the underlying domain model in the same way that the
-      regular UI would.</para>
-
-      <para>If using FitNesse on a "regular" project then the developer writes
-      glue code that in effect take the values out of the wiki page, and use
-      them to interact with the system. They then return a success/failure
-      response which FitNesse then uses to annotate the results page. FitNesse
-      calls this glue code a "fixture". There is some overlap with Naked
-      Objects' own use of that term. However, whereas a Naked Objects fixture
-      is only used to setup the initial state of a test, a FitNesse fixture
-      not only does that but it also is used to execute the test
-      proper.</para>
-
-      <para>It is relatively straightforward to integrate FitNesse into a
-      continuous integration environment; see <xref
-      linkend="sec.SettingUpContinuousIntegration" /> for further
-      details.</para>
+      <para>Scenario testing is closely related to (and sometimes a synonym
+      for) "agile acceptance testing"; I must admit though that I find that
+      term rather clumsy.</para>
     </sect1>
 
     <sect1>
-      <title>How Tested Objects' Integration Works</title>
-
-      <para>Although you could test a Naked Objects application using vanilla
-      FitNesse, this would entail you having to write all the FitNesse
-      fixtures to interact with the domain objects. You would also need to
-      come up with a representation for the tables.</para>
-
-      <para>Tested Objects' FitNesse integration is designed to let you use
-      FitNesse to test your Naked Objects application without all this hassle.
-      We can use the same Naked Objects metamodel that is used for the
-      auto-generation of its generic OOUIs to create a set of generic fixtures
-      that interact with the domain object similarly. Using Tested Objects is
-      therefore just a matter of using these fixtures out-of-the-box.</para>
-
-      <para>There are many such fixtures, but the one you will use the most
-      often is the UsingNakedObjectsViewer fixture (see <xref
-      linkend="sec.UsingNakedObjectsViewer" />). The screenshot below shows it
-      in use for checking assertions at the end of a test (the "Then").</para>
-
-      <screenshot>
-        <screeninfo>Before</screeninfo>
-
-        <mediaobject>
-          <imageobject>
-            <imagedata fileref="images/UsingNakedObjectsViewerBefore.png"
-                       scale="40" />
-          </imageobject>
-        </mediaobject>
-      </screenshot>
-
-      <para>To explain this: the "tomsClaim1" is an alias to an object that
-      has been created or obtained previously. The "perform" column lists the
-      verb to do, in this case all checks. Other things that can be done
-      include invoke actions, set properties and so forth. The "on member"
-      column specifies the property, collection or action of the domain object
-      being interacted with.</para>
-
-      <para>The following screenshot shows how the test is annotated after a
-      successful run:</para>
-
-      <screenshot>
-        <screeninfo>After</screeninfo>
-
-        <mediaobject>
-          <imageobject>
-            <imagedata fileref="images/UsingNakedObjectsViewerAfter.png"
-                       scale="40" />
-          </imageobject>
-        </mediaobject>
-      </screenshot>
-
-      <para>The end-user should be able to follow this test and could, if they
-      want, run through the same set of steps manually themselves. Or, of
-      course, they could just look at the test results.</para>
-    </sect1>
-  </chapter>
-
-  <chapter id="chp.UsingTheFitNesseArchetype">
-    <title>Using the FitNesse Archetype</title>
-
-    <para>Like Naked Objects, Tested Objects provides a Maven archetype to get
-    you up and running with quickly. Tested Objects' FitNesse archetype runs
-    against any Naked Objects application, and provides a new
-    <filename>xxx-fitnesse</filename> project (where <literal>xxx</literal> is
-    the root artifact Id). This project bundles FitNesse, contains the
-    FitNesse wiki pages that contain the tests (and also a user guide), and
-    allows the FitNesse wiki server to be run.</para>
-
-    <para>Out-of-the-box the archetype will generate a set of tests for the
-    example claims application in the
-    <filename>nakedobjects-4.0.x-for-maven.zip</filename> download<footnote>
-        <para>See <ulink
-        url="http://sourceforge.net/projects/nakedobjects">http://sourceforge.net/projects/nakedobjects</ulink></para>
-      </footnote>, under <filename>examples/claims</filename>). These are
-    great to try out <emphasis>Tested Objects</emphasis> for the first time,
-    and they also provide some ideas for you to structure your own test
-    suite.</para>
-
-    <para>The <filename>xxx-fitnesse</filename> project generated by the
-    archetype is intended to be included as a module of the parent pom
-    (<filename>xxx/pom.xml</filename>) --- in Maven parlance, it is a partial
-    archetype. Providing that your run the archetype in the correct directory,
-    Maven will automatically include the newly generated project in the parent
-    project.</para>
-
-    <sect1>
-      <title>Prerequisites</title>
-
-      <para>To use the archetype you'll need to install <ulink
-      url="http://maven.apache.org">Maven</ulink> (and there's a good chance
-      you've done this already if you're using the Naked Objects Maven
-      archetype).</para>
-
-      <para>Optionally (and recommended) you can install the <ulink
-      url="http://m2eclipse.sonatype.org/">m2eclipse</ulink> plugin for <ulink
-      url="http://eclipse.org">Eclipse</ulink> IDE (again, there's a good
-      chance you've done this too).</para>
-    </sect1>
-
-    <sect1>
-      <title>Running the Archetype</title>
-
-      <para>You can either run the archetype from the command line (and then
-      import into Eclipse if using m2eclipse), or run the archetype using
-      m2eclipse straight off.</para>
+      <title>Concordion Integration</title>
 
       <sect2>
-        <title>Running the archetype from the command line</title>
-
-        <para>Navigate to the parent directory of the project (that holds the
-        parent pom.xml). The directions here are for the examples/claims
-        project.</para>
-
-        <remark>TODO: Naked Objects 4.0.0 released examples/claims as
-        org.nakedobjects.distribution:examples-claims. In 4.0.1 (the plan is)
-        for it to be released as org.nakedobjects.examples:claims. In the
-        screenshots that follow I've hacked the 4.0.0 pom to change the
-        groupId and artifactId. However, I've provided the command that kicks
-        the whole thing for both 4.0.0 and 4.0.1</remark>
-
-        <para>To run the archetype from the command line (against NOF
-        4.0.0):</para>
-
-        <screen>$ cd $NO_HOME/examples-claims
-$ mvn archetype:generate \
-  -D archetypeCatalog=http://starobjects.sourceforge.net/m2-repo/snapshot \
-  -D archetypeGroupId=org.starobjects.tested.fitnesse \
-  -D archetypeArtifactId=archetype \
-  -D archetypeVersion=1.0-beta-3-SNAPSHOT \
-  -D groupId=org.nakedobjects.distribution \
-  -D artifactId=claims-fitnesse \
-  -D parentArtifactId=claims \
-  -D package=org.nakedobjects.examples.claims.fitnesse \
-  -D version=4.0.0</screen>
-
-        <para>or against NOF 4.0.1:</para>
-
-        <screen>$ cd $NO_HOME/examples-claims
-$ mvn archetype:generate \
-  -D archetypeCatalog=http://starobjects.sourceforge.net/m2-repo/snapshot \
-  -D archetypeGroupId=org.starobjects.tested.fitnesse \
-  -D archetypeArtifactId=archetype \
-  -D archetypeVersion=1.0-beta-3-SNAPSHOT \
-  -D groupId=org.nakedobjects.examples \
-  -D artifactId=claims-fitnesse \
-  -D parentArtifactId=claims \
-  -D package=org.nakedobjects.examples.claims.fitnesse \
-  -D version=4.0.1</screen>
-
-        <para>The groupId:parentArtifactId:version tuple should correspond to
-        the parent pom (so, for 4.0.0, that is
-        <classname>org.nakedobjects.distribution:claims:4.0.0</classname>, and
-        for 4.0.1 it is
-        <classname>org.nakedobjects.examples:claims:4.0.1</classname>). Note
-        that the parentArtifactId is not one of the standard Maven properties,
-        instead it is an additional property mandates by the Tested Objects'
-        archetype. (The archetype uses this property to derive the names of
-        some the other projects to include in the dependencies).</para>
-
-        <para>The artifactId then is the artifact of the FitNesse project
-        being generated, so is typically
-        <emphasis>parentname</emphasis>-fitnesse (so here:
-        <classname>claims-fitnesse</classname>).</para>
-
-        <para>The output is shown below:</para>
-
-        <para><screenshot>
-            <screeninfo>Generating the Maven Archetype from the command
-            line</screeninfo>
-
-            <mediaobject>
-              <imageobject>
-                <imagedata fileref="images/RunningMavenArchetypeFromCommandLine.png"
-                           scale="40" />
-              </imageobject>
-            </mediaobject>
-          </screenshot></para>
-
-        <para>This will generate a claims-fitnesse directory, and update the
-        parent pom.xml file.</para>
-
-        <para>We can then import into project into Eclipse, using File &gt;
-        Import &gt; Maven</para>
-
-        <screenshot>
-          <screeninfo>Importing the generated Maven Archetype into Eclipse (1
-          of 2)</screeninfo>
-
-          <mediaobject>
-            <imageobject>
-              <imagedata fileref="images/ImportingMavenProject.png" scale="40" />
-            </imageobject>
-          </mediaobject>
-        </screenshot>
+        <title>Introduction to Concordion</title>
 
-        <screenshot>
-          <screeninfo>Importing the generated Maven Archetype into Eclipse (2
-          of 2)</screeninfo>
-
-          <mediaobject>
-            <imageobject>
-              <imagedata fileref="images/ImportingMavenProject2.png"
-                         scale="40" />
-            </imageobject>
-          </mediaobject>
-        </screenshot>
+        <para><ulink url="http://concordion.org">Concordion</ulink> is a
+        framework to enable scenario testing. It is implemented as a <ulink
+        url="http://junit.org">JUnit4</ulink> test runner, with the test form
+        being written in <acronym>XHTML</acronym>. The domain expert /
+        business analyst authors new stories using an <acronym>XML</acronym>
+        editor (<ulink url="http://xmlmind.net">XmlMind</ulink> is one
+        commercial editor that we recommend); once executed as tests, the
+        results are shown as the same XHTML document, annotated to indicate
+        which assertions have succeeded, and which have failed. It also
+        creates an efficient feedback loop; a Concordion test will "keep on
+        going" even if it hits a failure. Thus the developer can identify
+        several issues and fix them in a single pass.</para>
+
+        <para>Another way to think of Concordion is as a replacement
+        presentation layer, hitting the underlying domain model in the same
+        way that the regular <acronym>UI</acronym> would. (This is why we call
+        this module is called the <acronym>BDD
+        </acronym><emphasis>viewer</emphasis>).</para>
+
+        <para><acronym>Concordion</acronym> works using a "convention over
+        configuration" approach, matching the <acronym>XHTML</acronym> text
+        file with a corresponding JUnit4 test run set up to run using
+        Concordion's <classname>ConcordionRunner</classname>, The developer
+        then annotates the <acronym>XHTML</acronym> using special
+        (Concordion-namespaced) attributes in order identify the inputs to and
+        expected results of the test. This is used by the
+        <classname>ConcordionRunner</classname> to call into corresponding
+        methods in the test.</para>
+
+        <para>For example, suppose the analyst writes a scenario test called
+        <filename>CustomerPlacesOrder.xhtml</filename>. In the
+        <acronym>XHTML</acronym> the analyst has identified the details of the
+        customer doing the ordering (customer ref 4321, say), the product
+        being ordered (product code 1234), the fact that the customer
+        initially has no orders, and that the customer has no invoices
+        outstanding. The test concludes with an assertion that there is now an
+        unfulfilled order for the customer, and that the customer now has an
+        invoice to be paid.</para>
+
+        <para>The developer in turn edits the <acronym>XHTML</acronym>,
+        identifying the customer and the product. He then further edits the
+        <acronym>XHTML</acronym> to call a method in the JUnit4 test
+        representing the placing of an order:
+        <methodname>placeOrder()</methodname>, say. And he finishes by
+        annotating the <acronym>XHTML</acronym> to make assertions about the
+        post conditions (unfulfilled order, new invoice to be paid
+        etc).</para>
+
+        <para>Then, the developer writes a JUnit4 test alongside the
+        <acronym>XHTML</acronym>; in this example it would be called
+        <filename>CustomerPlacesOrderTest.java</filename>. Concordion calls
+        into this JUnit4 test as it comes across the annotations in the
+        <acronym>XHTML</acronym>, and the JUnit4 test mediates with the system
+        under test.</para>
+
+        <para>When the test runs, Concordion generates a copy of the
+        <acronym>XHTML</acronym> in a output directory (by default specified
+        by a system property) which can then made available for inspection by
+        the business analyst (eg published on a website).</para>
+
+        <para>The Concordion website has a good <ulink
+        url="http://concordion.org/Tutorial.html">tutorial</ulink> that
+        demonstrates all the above, and can be completed in 20~30
+        minutes.</para>
       </sect2>
 
       <sect2>
-        <title>Running the archetype from m2eclipse</title>
-
-        <para>As an alternative to running the archetype from the command
-        line, you can instead run the archetype using m2eclipse.</para>
-
-        <para>However, m2eclipse does not (seem to) understand partial
-        archetypes, and so the project cannot be generated in a subfolder of
-        the parent project. Instead, I recommend you create the project
-        alongside the parent (eg in
-        <filename>examples/claims-fitnesse</filename>).</para>
-
-        <remark>TODO: These notes are slightly incomplete; it is also
-        necessary to specify the archetype catalog in the
-        Windows&gt;Preferences. However, m2eclipse 0.9.8 (the latest 'stable'
-        version at the time of writing) fails to pick up snapshot archetypes
-        from remote catalogs. The workaround is either to use the command line
-        once (this will copy the archetype into your local repository) or
-        alternatively achieve the same thing by building the FitNesse code
-        from scratch, as described in the developers' guide.</remark>
-
-        <para>So, use File &gt; New &gt; Project, then Maven &gt; Maven
-        Project to bring up the wizard:</para>
-
-        <screenshot>
-          <screeninfo>Generating the Maven Archetype using m2eclipse (1 of
-          4)</screeninfo>
-
-          <mediaobject>
-            <imageobject>
-              <imagedata fileref="images/m2eclipse.GenerateProject1.png"
-                         scale="40" />
-            </imageobject>
-          </mediaobject>
-        </screenshot>
-
-        <para>Specify the directory. Note that this cannot be a directory that
-        contains a pom.xml file.</para>
-
-        <screenshot>
-          <screeninfo>Generating the Maven Archetype using m2eclipse (2 of
-          4)</screeninfo>
-
-          <mediaobject>
-            <imageobject>
-              <imagedata fileref="images/m2eclipse.GenerateProject2.png"
-                         scale="40" />
-            </imageobject>
-          </mediaobject>
-        </screenshot>
-
-        <para>Select the archetype. (Note that the screenshot here shows the
-        archetype in the "Default Local" catalog ... that's because I've built
-        the archetype from source. ).</para>
+        <title>How the Isis/Concordion Integration Works</title>
 
-        <screenshot>
-          <screeninfo>Generating the Maven Archetype using m2eclipse (3 of
-          4)</screeninfo>
+        <para>Although you could test an Apache Isis application using vanilla
+        Concordion, this would entail you having to write all the glue code
+        yourself to interact with the domain objects. You would also need to
+        encode the rules that are normally implemented by the viewer, eg so
+        that a hidden action cannot be invoked, and an invalid value for a
+        property cannot be set.</para>
+
+        <para>The <emphasis>BDD viewer</emphasis> integration provided by
+        <emphasis>Apache Isis</emphasis> works by providing a superclass for
+        the JUnit4 test, called
+        <classname>AbstractIsisConcordionTest</classname>. This does several
+        things:</para>
+
+        <itemizedlist>
+          <listitem>
+            <para>it bootstraps an instance of <emphasis>Apache
+            Isis</emphasis> system using the in-memory object store;</para>
+          </listitem>
+
+          <listitem>
+            <para>the system is initialized with a set of services, picked up
+            from the <filename>isis.properties</filename> configuration
+            file</para>
+          </listitem>
+
+          <listitem>
+            <para>it provides methods to allow fixtures (domain objects) to be
+            installed into the object store</para>
+          </listitem>
+
+          <listitem>
+            <para>it provides methods to allow a user to be logged in, and the
+            date to be specified</para>
+          </listitem>
+
+          <listitem>
+            <para>it provides methods to allow the user to interact with
+            services and domain objects:</para>
+
+            <itemizedlist>
+              <listitem>
+                <para>asserting on the value of properties and the contents of
+                collections</para>
+              </listitem>
+
+              <listitem>
+                <para>setting the value of a property (if valid) and adding
+                to/removing from a collection (if valid)</para>
+              </listitem>
+
+              <listitem>
+                <para>invoking actions</para>
+              </listitem>
+
+              <listitem>
+                <para>asserting on the state of a class member (hidden,
+                disabled or enabled)</para>
+              </listitem>
+            </itemizedlist>
+          </listitem>
+        </itemizedlist>
+
+        <para>For each <acronym>XHTML</acronym> scenario test, the developer
+        writes subclasses the AbstractIsisConcordionTest, creating a name
+        matching the scenario test (ie as per regular Concordion). He
+        then</para>
+
+        <para>annotates the original <acronym>XHTML</acronym>, either calling
+        directly into the inherited methods, or writing small simple methods
+        to delegate to these inherited methods as required. The Concordion
+        website has some <ulink
+        url="http://concordion.org/Technique.html">hints and tips</ulink> to
+        help you find the right balance between these two approaches.</para>
+      </sect2>
+    </sect1>
+
+    <sect1>
+      <title>Common Library</title>
+
+      <para>The Concordion integration is based on a library that factors out
+      interactions with the running Isis system. This library is intended to
+      make it easy to support other BDD frameworks if required.<footnote>
+          <para>In fact, the first BDD framework integration was to <ulink
+          url="http://fitnesse.org">FitNesse</ulink>. This has now been
+          dropped because FitNesse has an incompatible license with Apache
+          Software Foundation. The original FitNesse code can still be found
+          on the <ulink
+          url="http://isis-contrib.sourceforge.net">Isis-contrib</ulink>
+          companion site, and does indeed use the common library.</para>
+        </footnote></para>
 
-          <mediaobject>
-            <imageobject>
-              <imagedata fileref="images/m2eclipse.GenerateProject3.png"
-                         scale="40" />
-            </imageobject>
-          </mediaobject>
-        </screenshot>
-
-        <para>Specify the groupId, artifactId (eg
-        <classname>claims-fitnesse</classname>), version and package. Also
-        specify the parentArtifactId as the parent project's artifactId (eg
-        <classname>claims</classname>):</para>
-
-        <screenshot>
-          <screeninfo>Generating the Maven Archetype using m2eclipse (4 of
-          4)</screeninfo>
-
-          <mediaobject>
-            <imageobject>
-              <imagedata fileref="images/m2eclipse.GenerateProject4.png"
-                         scale="40" />
-            </imageobject>
-          </mediaobject>
-        </screenshot>
-
-        <para>Hit Finish and you will be in more-or-less the same place as if
-        you had generated the archetype from the command line and then
-        imported. The only thing different is that the new fitnesse project
-        will not be in the parent project's modules.</para>
-
-        <para>Therefore, navigate to the parent project's
-        <sgmltag>&lt;modules&gt;</sgmltag> region, and add:</para>
-
-        <para><screen>&lt;modules&gt;
-    &lt;module&gt;dom&lt;/module&gt;
-    &lt;module&gt;fixture&lt;/module&gt;
-    &lt;module&gt;service&lt;/module&gt;
-    &lt;module&gt;commandline&lt;/module&gt;
-    &lt;module&gt;webapp&lt;/module&gt;
-    &lt;module&gt;../claims-fitnesse&lt;/module&gt;
-&lt;/modules&gt;</screen>The instructions in the wiki <emphasis>do</emphasis>
-        assume that you have done this.</para>
-      </sect2>
-    </sect1>
+      <para>The main concepts that the common library exposes are:</para>
 
-    <sect1>
-      <title>Starting FitNesse Wiki</title>
+      <itemizedlist>
+        <listitem>
+          <para>AliasItemsInListPeer</para>
+        </listitem>
 
-      <para>Once the fitnesse project has been imported you can start the
-      wiki. Navigate to <filename>ide/eclipse/launch</filename> and there will
-      be a launch configuration:</para>
-
-      <screenshot>
-        <screeninfo>Launch Configuratoin for the FitNesse wiki</screeninfo>
-
-        <mediaobject>
-          <imageobject>
-            <imagedata fileref="images/FitNesseWikiLaunchConfiguration.png"
-                       scale="40" />
-          </imageobject>
-        </mediaobject>
-      </screenshot>
-
-      <para>Start by right clicking and selecting run as. The FitNesse wiki
-      should start on port 9090:</para>
-
-      <screenshot>
-        <screeninfo>FitNesse Wiki running in the Eclipse console</screeninfo>
-
-        <mediaobject>
-          <imageobject>
-            <imagedata fileref="images/Console.FitNesseWikiServer.png"
-                       scale="40" />
-          </imageobject>
-        </mediaobject>
-      </screenshot>
-    </sect1>
+        <listitem>
+          <para>AliasServicesPeer</para>
+        </listitem>
 
-    <sect1>
-      <title>Setting up to Run Tests</title>
+        <listitem>
+          <para>CheckListPeer</para>
+        </listitem>
 
-      <para>Using your favorite browser, navigate to <ulink
-      url="http://localhost:9090/FrontPage">http://localhost:9090/FrontPage</ulink>;
-      you should see the introductory page:</para>
-
-      <screenshot>
-        <screeninfo>The FitNesse Wiki FrontPage</screeninfo>
-
-        <mediaobject>
-          <imageobject>
-            <imagedata fileref="images/FitNesseWikiFrontPage.png" scale="40" />
-          </imageobject>
-        </mediaobject>
-      </screenshot>
-
-      <para>Before we can run the generated tests there are a couple of things
-      we need to take care of (both are documented on FrontPage). First, we
-      need to copy the application into a location so that FitNesse can find
-      it. This is done by simply running mvn clean install on the
-      project:</para>
-
-      <screenshot>
-        <screeninfo>Running mvn clean install</screeninfo>
-
-        <mediaobject>
-          <imageobject>
-            <imagedata fileref="images/RunningMvnCleanInstall.png" scale="40" />
-          </imageobject>
-        </mediaobject>
-      </screenshot>
-
-      <para>(Note, if for any reason you didn't include the xxx-fitnesse
-      project into the parent project, you'll need to run mvn clean install
-      for the xxx-fitnesse project as well).</para>
-
-      <para>Secondly, the pages generated by the archetype use the
-      <filename>nakedobjects.properties</filename> to bootstrap Naked Objects.
-      By default, this is assumed to be in
-      <filename>../commandline/config/nakedobjects.properties</filename>. If
-      this isn't the case, then edit the BootStrapNakedObjects page as
-      required:</para>
-
-      <screenshot>
-        <screeninfo>Edit BootstrapNakedObjects if required</screeninfo>
-
-        <mediaobject>
-          <imageobject>
-            <imagedata fileref="images/EditBootstrapNakedObjects.png"
-                       scale="40" />
-          </imageobject>
-        </mediaobject>
-      </screenshot>
-    </sect1>
+        <listitem>
+          <para></para>
+        </listitem>
+      </itemizedlist>
 
-    <sect1>
-      <title>Running the Tests</title>
+      <para></para>
 
-      <para>If you are trying out the archetype against the examples/claims
-      application then you'll probably want to run the existing tests to check
-      everything works.</para>
-
-      <para>The tests are organized into a single application suite,
-      ClaimsAppSuite. This in turn defines two subsuites:</para>
-
-      <screenshot>
-        <screeninfo>The ClaimsAppSuite</screeninfo>
-
-        <mediaobject>
-          <imageobject>
-            <imagedata fileref="images/ClaimsAppSuite.png" scale="35" />
-          </imageobject>
-        </mediaobject>
-      </screenshot>
-
-      <para>FitNesse will automatically invoke all tests in the hierarchy,
-      therefore just press the Suite button on the left:</para>
-
-      <screenshot>
-        <screeninfo>The ClaimsAppSuite test results</screeninfo>
-
-        <mediaobject>
-          <imageobject>
-            <imagedata fileref="images/ClaimsAppSuite-TestResults.png"
-                       scale="35" />
-          </imageobject>
-        </mediaobject>
-      </screenshot>
-
-      <para>If you've run the archetype against your own project though, then
-      you can either delete these ClaimsAppSuites, or leave them around for
-      reference. To ensure that they don't accidentally run, use the
-      Properties button (on the left hand side) and deselect the Suite and
-      Test properties.</para>
+      <para></para>
     </sect1>
   </chapter>
 
@@ -815,7 +516,13 @@ $ mvn archetype:generate \
       page.</para>
 
       <sect2 id="sec.StoryFixture">
-        <title>StoryFixture</title>
+        <title>Story</title>
+
+        <para>Concordion: AbstractIsisConcordionTest</para>
+
+        <para>FitNesse: StoryFixture</para>
+
+        <para></para>
 
         <para>Sets up the workflow story test. Boilerplate, should always be
         the first FitNesse fixture included in a page.</para>
@@ -834,6 +541,14 @@ $ mvn archetype:generate \
       <sect2 id="sec.SetConfigDirectory">
         <title>SetConfigDirectory</title>
 
+        <para>Concordion:
+        AbstractIsisConcordionTest#bootstrapIsis(configDirectory,
+        deploymentType)</para>
+
+        <para>FitNesse: StoryFixture#setConfigDirectory</para>
+
+        <para></para>
+
         <para>Specifies the config directory containing
         <filename>nakedobjects.properties</filename>. Called after
         StoryFixture (see <xref linkend="sec.StoryFixture" />), and before
@@ -856,6 +571,14 @@ $ mvn archetype:generate \
       <sect2 id="sec.EnableExploration">
         <title>EnableExploration</title>
 
+        <para>Concordion:
+        AbstractIsisConcordionTest#bootstrapIsis(configDirectory,
+        deploymentType) ... derived from deploymentType</para>
+
+        <para>FitNesse: StoryFixture#enableExploration()</para>
+
+        <para></para>
+
         <para>Enables exploration actions if required. Should be called before
         InitNakedObjects (see <xref linkend="sec.InitNakedObjects" />).</para>
 
@@ -876,7 +599,15 @@ $ mvn archetype:generate \
       </sect2>
 
       <sect2 id="sec.InitNakedObjects">
-        <title>InitNakedObjects</title>
+        <title>InitIsis</title>
+
+        <para>Concordion:
+        AbstractIsisConcordionTest#bootstrapIsis(configDirectory,
+        deploymentType) ... derived from deploymentType</para>
+
+        <para>FitNesse: StoryFixture#initIsis()</para>
+
+        <para></para>
 
         <para>Initializes the Naked Objects runtime using the services
         specified through the UseConfigDirectory fixture (see <xref