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 2012/12/06 18:42:18 UTC

[31/52] [partial] ISIS-188: moving framework/ subdirs up to parent

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/UsingIsisViewerPeer.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/UsingIsisViewerPeer.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/UsingIsisViewerPeer.java
new file mode 100644
index 0000000..18981d1
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/UsingIsisViewerPeer.java
@@ -0,0 +1,386 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.isis.core.commons.exceptions.NotYetImplementedException;
+import org.apache.isis.core.commons.lang.StringUtils;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.facets.object.parseable.ParseableFacet;
+import org.apache.isis.core.metamodel.facets.object.parseable.TextEntryParseException;
+import org.apache.isis.core.metamodel.spec.ActionType;
+import org.apache.isis.core.metamodel.spec.ObjectActionSet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.feature.ObjectActionContainer.Contributed;
+import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.runtimes.dflt.runtime.system.DeploymentType;
+import org.apache.isis.viewer.bdd.common.AliasRegistry;
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.ScenarioBoundValueException;
+import org.apache.isis.viewer.bdd.common.ScenarioCell;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.AddToCollection;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.CheckAction;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.CheckAddToCollection;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.CheckClearProperty;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.CheckCollection;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.CheckObject;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.CheckProperty;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.CheckRemoveFromCollection;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.CheckSetProperty;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.ClearProperty;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.GetActionParameterChoices;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.GetActionParameterDefault;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.GetCollection;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.GetProperty;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.GetPropertyChoices;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.GetPropertyDefault;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.InvokeAction;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.Perform;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.PerformContext;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.RemoveFromCollection;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.SaveObject;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.SetProperty;
+import org.apache.isis.viewer.bdd.common.parsers.DateParser;
+
+public class UsingIsisViewerPeer extends AbstractFixturePeer {
+
+    private static List<Perform> performCommands(final Perform.Mode mode) {
+        final ArrayList<Perform> commands = new ArrayList<Perform>();
+
+        commands.add(new CheckProperty(mode));
+        commands.add(new CheckSetProperty(mode));
+        commands.add(new CheckClearProperty(mode));
+        commands.add(new GetProperty(mode));
+        commands.add(new SetProperty(mode));
+        commands.add(new ClearProperty(mode));
+        commands.add(new GetPropertyDefault(mode));
+        commands.add(new GetPropertyChoices(mode));
+
+        commands.add(new CheckCollection(mode));
+        commands.add(new CheckAddToCollection(mode));
+        commands.add(new CheckRemoveFromCollection(mode));
+        commands.add(new AddToCollection(mode));
+        commands.add(new RemoveFromCollection(mode));
+        commands.add(new GetCollection(mode));
+
+        commands.add(new CheckAction(mode));
+        commands.add(new InvokeAction(mode));
+        commands.add(new GetActionParameterDefault(mode));
+        commands.add(new GetActionParameterChoices(mode));
+
+        commands.add(new CheckObject(mode));
+        commands.add(new SaveObject(mode));
+
+        return commands;
+    }
+
+    // //////////////////////////////////////////////////////////////////
+    // constructor
+    // //////////////////////////////////////////////////////////////////
+
+    private final CellBinding onObjectBinding;
+    private final CellBinding aliasResultAsBinding;
+    private final CellBinding performBinding;
+    private final CellBinding onMemberBinding;
+    private final CellBinding thatItBinding;
+    private final CellBinding arg0Binding;
+
+    private final DeploymentType deploymentType;
+    private final DateParser dateParser;
+
+    private final Map<String, Perform> commandByKey = new HashMap<String, Perform>();
+
+    public UsingIsisViewerPeer(final AliasRegistry aliasesRegistry, final DeploymentType deploymentType, final DateParser dateParser, final Perform.Mode mode, final CellBinding onObjectBinding, final CellBinding aliasResultAsBinding, final CellBinding performBinding,
+            final CellBinding onMemberBinding, final CellBinding thatItBinding, final CellBinding arg0Binding) {
+        super(aliasesRegistry, onObjectBinding, aliasResultAsBinding, performBinding, onMemberBinding, thatItBinding, arg0Binding);
+
+        this.onObjectBinding = onObjectBinding;
+        this.aliasResultAsBinding = aliasResultAsBinding;
+        this.performBinding = performBinding;
+        this.onMemberBinding = onMemberBinding;
+        this.thatItBinding = thatItBinding;
+        this.arg0Binding = arg0Binding;
+
+        this.deploymentType = deploymentType;
+        this.dateParser = dateParser;
+
+        final List<Perform> performCommands = performCommands(mode);
+        for (final Perform command : performCommands) {
+            commandByKey.put(command.getKey(), command);
+        }
+    }
+
+    public DeploymentType getDeploymentType() {
+        return deploymentType;
+    }
+
+    public DateParser getDateParser() {
+        return dateParser;
+    }
+
+    public CellBinding getOnObjectBinding() {
+        return onObjectBinding;
+    }
+
+    public CellBinding getAliasResultAsBinding() {
+        return aliasResultAsBinding;
+    }
+
+    public CellBinding getOnMemberBinding() {
+        return onMemberBinding;
+    }
+
+    public CellBinding getPerformBinding() {
+        return performBinding;
+    }
+
+    public CellBinding getThatItBinding() {
+        return thatItBinding;
+    }
+
+    public CellBinding getArg0Binding() {
+        return arg0Binding;
+    }
+
+    public boolean isArg0BindingLast() {
+        return !bindingAfterArg0();
+
+    }
+
+    private boolean bindingAfterArg0() {
+        if (!getArg0Binding().isFound()) {
+            return false;
+        }
+        for (final CellBinding binding : getCellBindings()) {
+            if (binding.getColumn() > getArg0Binding().getColumn()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // //////////////////////////////////////////////////////////////////
+    // validate API
+    // //////////////////////////////////////////////////////////////////
+
+    public ObjectAdapter validateOnObject() throws ScenarioBoundValueException {
+
+        final ScenarioCell onObjectCell = onObjectBinding.getCurrentCell();
+        String onObject = onObjectCell.getText();
+        if (onObject == null) {
+            if (previousOnObject == null) {
+                throw ScenarioBoundValueException.current(onObjectBinding, "(required)");
+            }
+            onObject = previousOnObject;
+        } else {
+            previousOnObject = onObject;
+        }
+        final ObjectAdapter onAdapter = getAliasRegistry().getAliased(onObject);
+        if (onAdapter == null) {
+            throw ScenarioBoundValueException.current(onMemberBinding, "(unknown object)");
+        }
+        return onAdapter;
+    }
+
+    public String validateAliasAs() throws ScenarioBoundValueException {
+        if (getAliasResultAsBinding() == null) {
+            return null;
+        }
+        final ScenarioCell aliasCell = aliasResultAsBinding.getCurrentCell();
+        if (aliasCell == null) {
+            return null;
+        }
+
+        final String aliasAs = aliasCell.getText();
+        if (getAliasRegistry().getAliased(aliasAs) != null) {
+            throw ScenarioBoundValueException.current(aliasResultAsBinding, "(already used)");
+        }
+        return aliasAs;
+    }
+
+    public ObjectMember validateOnMember(final ObjectAdapter onAdapter) throws ScenarioBoundValueException {
+
+        final ScenarioCell onMemberCell = onMemberBinding.getCurrentCell();
+        final String onMember = onMemberCell.getText();
+
+        if (StringUtils.isNullOrEmpty(onMember)) {
+            throw ScenarioBoundValueException.current(onMemberBinding, "(required)");
+        }
+
+        if (onAdapter == null) {
+            return null;
+        }
+
+        // see if property, collection or action.
+        final String memberId = StringUtils.memberIdFor(onMember);
+        final ObjectSpecification spec = onAdapter.getSpecification();
+        final List<ObjectMember> objectMembers = new ArrayList<ObjectMember>();
+
+        objectMembers.addAll(spec.getAssociations());
+
+        // see if action (of any type)
+        objectMembers.addAll(spec.getObjectActions(Arrays.asList(ActionType.USER, ActionType.EXPLORATION, ActionType.DEBUG), Contributed.INCLUDED));
+        for (final ObjectMember member : objectMembers) {
+            if (matchesId(member, memberId)) {
+                return member;
+            }
+            // special handling for contributed actions.
+            if (member instanceof ObjectActionSet) {
+                final ObjectActionSet actionSet = (ObjectActionSet) member;
+                for (final ObjectAction contributedAction : actionSet.getActions()) {
+                    if (contributedAction.getId().equals(memberId)) {
+                        return contributedAction;
+                    }
+                }
+            }
+        }
+        throw ScenarioBoundValueException.current(onMemberBinding, "(unknown member)");
+    }
+
+    public Perform validatePerform() throws ScenarioBoundValueException {
+        final String perform = performBinding.getCurrentCell().getText();
+        if (perform == null) {
+            throw ScenarioBoundValueException.current(performBinding, "(required)");
+        }
+        final Perform performCommand = commandByKey.get(perform);
+        if (performCommand == null) {
+            throw ScenarioBoundValueException.current(performBinding, "(unknown interaction)");
+        }
+        return performCommand;
+    }
+
+    private boolean matchesId(final ObjectMember member, final String memberId) {
+        return member.getId().equals(memberId);
+    }
+
+    // //////////////////////////////////////////////////////////////////
+    // "perform" API
+    // //////////////////////////////////////////////////////////////////
+
+    public void performCommand(final ObjectAdapter onAdapter, final String aliasAs, final ObjectMember objectMember, final Perform performCommand, final List<ScenarioCell> argumentStoryCells) throws ScenarioBoundValueException {
+        final PerformContext performContext = new PerformContext(this, onAdapter, objectMember, argumentStoryCells);
+        try {
+            performCommand.perform(performContext);
+        } catch (final RuntimeException ex) {
+            // handler should have colored in invalid cells.
+        }
+        aliasResultFromPerformCommand(performCommand, aliasAs);
+    }
+
+    private void aliasResultFromPerformCommand(final Perform performCommand, final String aliasAs) throws ScenarioBoundValueException {
+        if (StringUtils.isNullOrEmpty(aliasAs)) {
+            return;
+        }
+        final ObjectAdapter resultAdapter = performCommand.getResult();
+        if (resultAdapter == null) {
+            throw ScenarioBoundValueException.current(onMemberBinding, "(no result)");
+        }
+        getAliasRegistry().aliasAs(aliasAs, resultAdapter);
+    }
+
+    // //////////////////////////////////////////////////////////////////
+    //
+    // //////////////////////////////////////////////////////////////////
+
+    private String previousOnObject = null;
+
+    /**
+     * Not public API
+     */
+    public void provideDefault(final ScenarioCell storySource, final String resultStr) {
+        // TODO Auto-generated method stub
+        throw new NotYetImplementedException();
+    }
+
+    /**
+     * Not public API
+     */
+    public ObjectAdapter getAdapter(final ObjectAdapter contextAdapter, final ObjectSpecification noSpec, final CellBinding contextBinding, final ScenarioCell paramCell) throws ScenarioBoundValueException {
+
+        final String cellText = paramCell.getText();
+
+        // see if can handle as parseable value
+        final ParseableFacet parseableFacet = noSpec.getFacet(ParseableFacet.class);
+        if (parseableFacet != null) {
+            try {
+                return parseableFacet.parseTextEntry(contextAdapter, cellText, null);
+            } catch (final TextEntryParseException ex) {
+                throw ScenarioBoundValueException.arg(contextBinding, paramCell, "(cannot parse '" + cellText + "')");
+            } catch (final IllegalArgumentException ex) {
+                // REVIEW: isn't what is thrown, but perhaps
+                // TextEntryParseException should inherit from
+                // IllegalArgumentException?
+                throw ScenarioBoundValueException.arg(contextBinding, paramCell, "(cannot parse '" + cellText + "')");
+            }
+        }
+
+        // otherwise, handle as reference to known object
+        final ObjectAdapter adapter = getAliasRegistry().getAliased(cellText);
+        if (adapter == null) {
+            throw ScenarioBoundValueException.arg(contextBinding, paramCell, "(unknown reference'" + cellText + "')");
+        }
+
+        return adapter;
+    }
+
+    /**
+     * Not public API
+     * 
+     * <p>
+     * Ensures that there are at least enough arguments for the number of
+     * parameters required.
+     */
+    public ObjectAdapter[] getAdapters(final ObjectAdapter onAdapter, final ObjectAction objectAction, final CellBinding onMemberBinding, final List<ScenarioCell> argumentCells) throws ScenarioBoundValueException {
+        final List<ObjectActionParameter> parameters = objectAction.getParameters();
+
+        final int parameterCount = parameters.size();
+        if (argumentCells.size() < parameterCount) {
+            throw ScenarioBoundValueException.current(onMemberBinding, "(action requires " + parameterCount + " arguments)");
+        }
+        final ObjectAdapter[] adapters = new ObjectAdapter[parameterCount];
+
+        for (int i = 0; i < parameterCount; i++) {
+            final ScenarioCell paramCell = argumentCells.get(i);
+            final ObjectActionParameter parameter = parameters.get(i);
+            adapters[i] = getAdapter(null, parameter.getSpecification(), onMemberBinding, paramCell);
+        }
+        return adapters;
+    }
+
+    /**
+     * Not public API
+     */
+    public ObjectAdapter toAdaptedListOfPojos(final ObjectAdapter[] choiceAdapters) {
+        final List<Object> choiceList = new ArrayList<Object>();
+        if (choiceAdapters != null) {
+            for (final ObjectAdapter adapter : choiceAdapters) {
+                choiceList.add(adapter.getObject());
+            }
+        }
+        return getAdapterManager().adapterFor(choiceList);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/AddToCollection.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/AddToCollection.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/AddToCollection.java
new file mode 100644
index 0000000..1dff803
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/AddToCollection.java
@@ -0,0 +1,79 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.consent.InteractionInvocationMethod;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionAddToFacet;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.ScenarioBoundValueException;
+import org.apache.isis.viewer.bdd.common.ScenarioCell;
+
+public class AddToCollection extends PerformAbstractTypeParams {
+
+    private ObjectAdapter result;
+
+    public AddToCollection(final Perform.Mode mode) {
+        super("add to collection", Type.COLLECTION, NumParameters.ONE, mode);
+    }
+
+    @Override
+    public void doHandle(final PerformContext performContext) throws ScenarioBoundValueException {
+
+        final ObjectAdapter onAdapter = performContext.getOnAdapter();
+        final ObjectMember nakedObjectMember = performContext.getObjectMember();
+        final CellBinding onMemberBinding = performContext.getPeer().getOnMemberBinding();
+
+        final OneToManyAssociation otma = (OneToManyAssociation) nakedObjectMember;
+
+        final CollectionAddToFacet addToFacet = nakedObjectMember.getFacet(CollectionAddToFacet.class);
+        if (addToFacet == null) {
+            throw ScenarioBoundValueException.current(onMemberBinding, "(cannot add to collection)");
+        }
+
+        // safe since guaranteed by superclass
+        final CellBinding arg0Binding = performContext.getPeer().getArg0Binding();
+        final ScenarioCell arg0Cell = arg0Binding.getCurrentCell();
+        final String toAddAlias = arg0Cell.getText();
+
+        final ObjectAdapter toAddAdapter = performContext.getPeer().getAliasRegistry().getAliased(toAddAlias);
+        if (toAddAdapter == null) {
+            throw ScenarioBoundValueException.current(arg0Binding, "(unknown alias)");
+        }
+
+        // validate argument
+        otma.createValidateAddInteractionContext(getSession(), InteractionInvocationMethod.BY_USER, onAdapter, toAddAdapter);
+        final Consent validToAdd = otma.isValidToAdd(onAdapter, toAddAdapter);
+        if (validToAdd.isVetoed()) {
+            throw ScenarioBoundValueException.current(arg0Binding, validToAdd.getReason());
+        }
+
+        // add
+        addToFacet.add(onAdapter, toAddAdapter);
+    }
+
+    @Override
+    public ObjectAdapter getResult() {
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckAction.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckAction.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckAction.java
new file mode 100644
index 0000000..4359027
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckAction.java
@@ -0,0 +1,35 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.Disabled;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.Hidden;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.PerformCheckThatAbstract;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.Usable;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.Visible;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.action.ArgumentSetNotValid;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.action.ArgumentSetValid;
+
+public class CheckAction extends PerformCheckThatAbstract {
+
+    public CheckAction(final Perform.Mode mode) {
+        super("check action", OnMemberColumn.REQUIRED, mode, new Hidden(), new Visible(), new Disabled(), new Usable(), new ArgumentSetValid(), new ArgumentSetNotValid());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckAddToCollection.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckAddToCollection.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckAddToCollection.java
new file mode 100644
index 0000000..2e35ebe
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckAddToCollection.java
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.AssertsValidity;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.PerformCheckThatAbstract;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.collections.ProposedAddTo;
+
+public class CheckAddToCollection extends PerformCheckThatAbstract {
+
+    public CheckAddToCollection(final Perform.Mode mode) {
+        super("check add to collection", OnMemberColumn.REQUIRED, mode, new ProposedAddTo(AssertsValidity.VALID), new ProposedAddTo(AssertsValidity.INVALID));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckClearProperty.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckClearProperty.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckClearProperty.java
new file mode 100644
index 0000000..865163b
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckClearProperty.java
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.AssertsValidity;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.PerformCheckThatAbstract;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.property.ProposedClear;
+
+public class CheckClearProperty extends PerformCheckThatAbstract {
+
+    public CheckClearProperty(final Perform.Mode mode) {
+        super("check clear property", OnMemberColumn.REQUIRED, mode, new ProposedClear(AssertsValidity.VALID), new ProposedClear(AssertsValidity.INVALID));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckCollection.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckCollection.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckCollection.java
new file mode 100644
index 0000000..08fa553
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckCollection.java
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.AssertsContainment;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.AssertsEmpty;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.Disabled;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.Hidden;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.PerformCheckThatAbstract;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.Usable;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.Visible;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.collections.Containment;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.collections.Emptiness;
+
+public class CheckCollection extends PerformCheckThatAbstract {
+
+    public CheckCollection(final Perform.Mode mode) {
+        super("check collection", OnMemberColumn.REQUIRED, mode, new Hidden(), new Visible(), new Disabled(), new Usable(), new Emptiness(AssertsEmpty.EMPTY), new Emptiness(AssertsEmpty.NOT_EMPTY), new Containment(AssertsContainment.CONTAINS), new Containment(AssertsContainment.DOES_NOT_CONTAIN));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckObject.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckObject.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckObject.java
new file mode 100644
index 0000000..93d677a
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckObject.java
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.PerformCheckThatAbstract;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.object.NotSaved;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.object.NotValid;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.object.Saved;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.object.Valid;
+
+public class CheckObject extends PerformCheckThatAbstract {
+
+    public CheckObject(final Perform.Mode mode) {
+        super("check object", OnMemberColumn.NOT_REQUIRED, mode, new Valid(), new NotValid(), new NotSaved(), new Saved());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckProperty.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckProperty.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckProperty.java
new file mode 100644
index 0000000..e69d8c6
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckProperty.java
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.Disabled;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.Hidden;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.PerformCheckThatAbstract;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.Usable;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.Visible;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.property.Contains;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.property.DoesNotContain;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.property.Empty;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.property.NotEmpty;
+
+public class CheckProperty extends PerformCheckThatAbstract {
+
+    public CheckProperty(final Perform.Mode mode) {
+        super("check property", OnMemberColumn.REQUIRED, mode, new Hidden(), new Visible(), new Disabled(), new Usable(), new Contains(), new DoesNotContain(), new Empty(), new NotEmpty());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckRemoveFromCollection.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckRemoveFromCollection.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckRemoveFromCollection.java
new file mode 100644
index 0000000..a39c7fb
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckRemoveFromCollection.java
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.AssertsValidity;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.PerformCheckThatAbstract;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.collections.ProposedRemoveFrom;
+
+public class CheckRemoveFromCollection extends PerformCheckThatAbstract {
+
+    public CheckRemoveFromCollection(final Perform.Mode mode) {
+        super("check remove from collection", OnMemberColumn.REQUIRED, mode, new ProposedRemoveFrom(AssertsValidity.VALID), new ProposedRemoveFrom(AssertsValidity.INVALID));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckSetProperty.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckSetProperty.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckSetProperty.java
new file mode 100644
index 0000000..ea4a853
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/CheckSetProperty.java
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.AssertsValidity;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.PerformCheckThatAbstract;
+import org.apache.isis.viewer.bdd.common.fixtures.perform.checkthat.property.ProposedSet;
+
+public class CheckSetProperty extends PerformCheckThatAbstract {
+
+    public CheckSetProperty(final Perform.Mode mode) {
+        super("check set property", OnMemberColumn.REQUIRED, mode, new ProposedSet(AssertsValidity.VALID), new ProposedSet(AssertsValidity.INVALID));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/ClearProperty.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/ClearProperty.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/ClearProperty.java
new file mode 100644
index 0000000..2568d42
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/ClearProperty.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.facets.properties.modify.PropertyClearFacet;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.ScenarioBoundValueException;
+
+public class ClearProperty extends PerformAbstractTypeParams {
+
+    private ObjectAdapter result;
+
+    public ClearProperty(final Perform.Mode mode) {
+        super("clear property", Type.PROPERTY, NumParameters.ZERO, mode);
+    }
+
+    @Override
+    public void doHandle(final PerformContext performContext) throws ScenarioBoundValueException {
+
+        final ObjectAdapter onAdapter = performContext.getOnAdapter();
+        final ObjectMember nakedObjectMember = performContext.getObjectMember();
+
+        final OneToOneAssociation otoa = (OneToOneAssociation) nakedObjectMember;
+
+        // set
+        final PropertyClearFacet clearFacet = otoa.getFacet(PropertyClearFacet.class);
+        final CellBinding thatItBinding = performContext.getPeer().getThatItBinding();
+        if (clearFacet == null) {
+            throw ScenarioBoundValueException.current(thatItBinding, "(cannot clear)");
+        }
+
+        // validate setting to null
+        final Consent validConsent = otoa.isAssociationValid(onAdapter, null);
+        if (validConsent.isVetoed()) {
+            throw ScenarioBoundValueException.current(thatItBinding, validConsent.getReason());
+        }
+
+        clearFacet.clearProperty(onAdapter);
+
+    }
+
+    @Override
+    public ObjectAdapter getResult() {
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetActionParameterChoices.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetActionParameterChoices.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetActionParameterChoices.java
new file mode 100644
index 0000000..bf43aa1
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetActionParameterChoices.java
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.ScenarioBoundValueException;
+import org.apache.isis.viewer.bdd.common.ScenarioCell;
+
+public class GetActionParameterChoices extends PerformAbstractTypeParams {
+
+    private ObjectAdapter result;
+
+    public GetActionParameterChoices(final Perform.Mode mode) {
+        super("get action parameter choices", Type.ACTION, NumParameters.ONE, mode);
+    }
+
+    @Override
+    public void doHandle(final PerformContext performContext) throws ScenarioBoundValueException {
+
+        final ObjectAdapter onAdapter = performContext.getOnAdapter();
+        final ObjectMember nakedObjectMember = performContext.getObjectMember();
+        final CellBinding arg0Binding = performContext.getPeer().getArg0Binding();
+        final ScenarioCell arg0Cell = arg0Binding.getCurrentCell();
+
+        int requestedParamNum = -1;
+        try {
+            requestedParamNum = Integer.valueOf(arg0Cell.getText());
+        } catch (final NumberFormatException ex) {
+            throw ScenarioBoundValueException.current(arg0Binding, ex.getMessage());
+        }
+
+        final ObjectAction noa = (ObjectAction) nakedObjectMember;
+        final int parameterCount = noa.getParameterCount();
+        if (requestedParamNum < 0 || requestedParamNum > parameterCount - 1) {
+            throw ScenarioBoundValueException.current(arg0Binding, "(must be between 0 and " + (parameterCount - 1) + ")");
+        }
+
+        final ObjectAdapter[][] allParameterChoices = noa.getChoices(onAdapter);
+        result = performContext.getPeer().toAdaptedListOfPojos(allParameterChoices[requestedParamNum]);
+    }
+
+    @Override
+    public ObjectAdapter getResult() {
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetActionParameterDefault.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetActionParameterDefault.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetActionParameterDefault.java
new file mode 100644
index 0000000..f440d1b
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetActionParameterDefault.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.ScenarioBoundValueException;
+import org.apache.isis.viewer.bdd.common.ScenarioCell;
+
+public class GetActionParameterDefault extends PerformAbstractTypeParams {
+
+    private ObjectAdapter result;
+
+    public GetActionParameterDefault(final Perform.Mode mode) {
+        super("get action parameter default", Type.ACTION, NumParameters.ONE, mode);
+    }
+
+    @Override
+    public void doHandle(final PerformContext performContext) throws ScenarioBoundValueException {
+
+        final ObjectAdapter onAdapter = performContext.getOnAdapter();
+        final ObjectMember nakedObjectMember = performContext.getObjectMember();
+        final CellBinding arg0Binding = performContext.getPeer().getArg0Binding();
+        final ScenarioCell arg0Cell = arg0Binding.getCurrentCell();
+        int requestedParamNum = -1;
+        try {
+            requestedParamNum = Integer.valueOf(arg0Cell.getText());
+        } catch (final NumberFormatException ex) {
+            throw ScenarioBoundValueException.current(arg0Binding, ex.getMessage());
+        }
+
+        final ObjectAction noa = (ObjectAction) nakedObjectMember;
+        final int parameterCount = noa.getParameterCount();
+        if (requestedParamNum < 0 || requestedParamNum > parameterCount - 1) {
+            throw ScenarioBoundValueException.current(arg0Binding, "(must be between 0 and " + (parameterCount - 1) + ")");
+        }
+
+        final ObjectAdapter[] defaults = noa.getDefaults(onAdapter);
+        result = defaults[requestedParamNum];
+    }
+
+    @Override
+    public ObjectAdapter getResult() {
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetCollection.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetCollection.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetCollection.java
new file mode 100644
index 0000000..47a8282
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetCollection.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+
+public class GetCollection extends PerformAbstractTypeParams {
+
+    private ObjectAdapter result;
+
+    public GetCollection(final Perform.Mode mode) {
+        super("get collection", Type.COLLECTION, NumParameters.ZERO, mode);
+    }
+
+    @Override
+    public void doHandle(final PerformContext performContext) {
+
+        final ObjectAdapter onAdapter = performContext.getOnAdapter();
+        final ObjectMember nakedObjectMember = performContext.getObjectMember();
+
+        final OneToManyAssociation otma = (OneToManyAssociation) nakedObjectMember;
+
+        result = otma.get(onAdapter);
+    }
+
+    @Override
+    public ObjectAdapter getResult() {
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetProperty.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetProperty.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetProperty.java
new file mode 100644
index 0000000..f0cc55e
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetProperty.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+
+public class GetProperty extends PerformAbstractTypeParams {
+
+    private ObjectAdapter result;
+
+    public GetProperty(final Perform.Mode mode) {
+        super("get property", Type.PROPERTY, NumParameters.ZERO, mode);
+    }
+
+    @Override
+    public void doHandle(final PerformContext performContext) {
+
+        final ObjectAdapter onAdapter = performContext.getOnAdapter();
+        final ObjectMember nakedObjectMember = performContext.getObjectMember();
+
+        final OneToOneAssociation otoa = (OneToOneAssociation) nakedObjectMember;
+
+        result = otoa.get(onAdapter);
+    }
+
+    @Override
+    public ObjectAdapter getResult() {
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetPropertyChoices.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetPropertyChoices.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetPropertyChoices.java
new file mode 100644
index 0000000..e901ccc
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetPropertyChoices.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+
+public class GetPropertyChoices extends PerformAbstractTypeParams {
+
+    private ObjectAdapter result;
+
+    public GetPropertyChoices(final Perform.Mode mode) {
+        super("get property choices", Type.PROPERTY, NumParameters.ZERO, mode);
+    }
+
+    @Override
+    public void doHandle(final PerformContext performContext) {
+
+        final ObjectAdapter onAdapter = performContext.getOnAdapter();
+        final ObjectMember nakedObjectMember = performContext.getObjectMember();
+
+        final OneToOneAssociation otoa = (OneToOneAssociation) nakedObjectMember;
+
+        result = performContext.getPeer().toAdaptedListOfPojos(otoa.getChoices(onAdapter));
+    }
+
+    @Override
+    public ObjectAdapter getResult() {
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetPropertyDefault.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetPropertyDefault.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetPropertyDefault.java
new file mode 100644
index 0000000..2766e41
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/GetPropertyDefault.java
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+
+public class GetPropertyDefault extends PerformAbstractTypeParams {
+
+    private ObjectAdapter result;
+
+    public GetPropertyDefault(final Perform.Mode mode) {
+        super("get property default", Type.PROPERTY, NumParameters.ZERO, mode);
+    }
+
+    @Override
+    public void doHandle(final PerformContext performContext) {
+
+        final ObjectAdapter onAdapter = performContext.getOnAdapter();
+        final ObjectMember nakedObjectMember = performContext.getObjectMember();
+
+        final OneToOneAssociation otoa = (OneToOneAssociation) nakedObjectMember;
+
+        // TODO: the OTOA interface is wrong, should be declared as returning a
+        // NakedObject
+        // (which is indeed what the implementation does)
+        result = otoa.getDefault(onAdapter);
+    }
+
+    @Override
+    public ObjectAdapter getResult() {
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/InvokeAction.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/InvokeAction.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/InvokeAction.java
new file mode 100644
index 0000000..c3c7fe6
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/InvokeAction.java
@@ -0,0 +1,78 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import java.util.List;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.ScenarioBoundValueException;
+import org.apache.isis.viewer.bdd.common.ScenarioCell;
+
+public class InvokeAction extends PerformAbstractTypeParams {
+
+    private ObjectAdapter result;
+
+    public InvokeAction(final Perform.Mode mode) {
+        super("invoke action", Type.ACTION, NumParameters.UNLIMITED, mode);
+    }
+
+    @Override
+    public void doHandle(final PerformContext performContext) throws ScenarioBoundValueException {
+
+        final ObjectAdapter onAdapter = performContext.getOnAdapter();
+        final ObjectMember objectMember = performContext.getObjectMember();
+        final CellBinding onMemberBinding = performContext.getPeer().getOnMemberBinding();
+        final List<ScenarioCell> argumentCells = performContext.getArgumentCells();
+
+        final ObjectAction objectAction = (ObjectAction) objectMember;
+
+        final int parameterCount = objectAction.getParameterCount();
+        final boolean isContributedOneArgAction = objectAction.isContributed() && parameterCount == 1;
+
+        ObjectAdapter[] proposedArguments;
+        if (!isContributedOneArgAction) {
+
+            // lookup arguments
+            proposedArguments = performContext.getPeer().getAdapters(onAdapter, objectAction, onMemberBinding, argumentCells);
+
+            // validate arguments
+            final Consent argSetValid = objectAction.isProposedArgumentSetValid(onAdapter, proposedArguments);
+            if (argSetValid.isVetoed()) {
+                throw ScenarioBoundValueException.current(onMemberBinding, argSetValid.getReason());
+            }
+        } else {
+            proposedArguments = new ObjectAdapter[] { onAdapter };
+        }
+
+        // execute
+        result = objectAction.execute(onAdapter, proposedArguments);
+
+        // all OK.
+    }
+
+    @Override
+    public ObjectAdapter getResult() {
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/Perform.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/Perform.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/Perform.java
new file mode 100644
index 0000000..10a1c7d
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/Perform.java
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.viewer.bdd.common.ScenarioBoundValueException;
+
+public interface Perform {
+
+    public static enum Mode {
+        SETUP, TEST
+    }
+
+    String getKey();
+
+    void perform(PerformContext performContext) throws ScenarioBoundValueException;
+
+    ObjectAdapter getResult();
+
+    boolean requiresMember();
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformAbstract.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformAbstract.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformAbstract.java
new file mode 100644
index 0000000..669aa1f
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformAbstract.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.core.commons.authentication.AuthenticationSession;
+import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext;
+
+public abstract class PerformAbstract implements Perform {
+
+    private final String key;
+    private final Perform.Mode mode;
+
+    public PerformAbstract(final String key, final Perform.Mode mode) {
+        this.key = key;
+        this.mode = mode;
+    }
+
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+    protected Perform.Mode getMode() {
+        return mode;
+    }
+
+    protected AuthenticationSession getSession() {
+        return IsisContext.getAuthenticationSession();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformAbstractTypeParams.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformAbstractTypeParams.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformAbstractTypeParams.java
new file mode 100644
index 0000000..2e0d1fa
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformAbstractTypeParams.java
@@ -0,0 +1,188 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.ScenarioBoundValueException;
+import org.apache.isis.viewer.bdd.common.ScenarioCell;
+
+public abstract class PerformAbstractTypeParams extends PerformAbstract {
+
+    protected static enum Type {
+        PROPERTY(true) {
+            @Override
+            String ensureMemberIsOfType(final ObjectMember m) {
+                return m instanceof OneToOneAssociation ? null : "(not a property)";
+            }
+        },
+        COLLECTION(true) {
+            @Override
+            String ensureMemberIsOfType(final ObjectMember m) {
+                return m instanceof OneToManyAssociation ? null : "(not a collection)";
+            }
+        },
+        ACTION(true) {
+            @Override
+            String ensureMemberIsOfType(final ObjectMember m) {
+                return m instanceof ObjectAction ? null : "(not an action)";
+            }
+        },
+        OBJECT(false) {
+            @Override
+            String ensureMemberIsOfType(final ObjectMember m) {
+                return m != null ? "(not required)" : null;
+            }
+        };
+        // public void ensureMemberIsOfType(
+        // final NakedObjectMember nakedObjectMember,
+        // final StoryCell onMemberCell) throws StoryFailureException {
+        // final String msg = ensureMemberIsOfType(nakedObjectMember);
+        // if (msg != null) {
+        // throw new StoryFailureException(onMemberCell, msg);
+        // }
+        // }
+
+        abstract String ensureMemberIsOfType(ObjectMember m);
+
+        private final boolean representsMember;
+
+        private Type(final boolean representsMember) {
+            this.representsMember = representsMember;
+        }
+
+        public boolean representsMember() {
+            return representsMember;
+        }
+    }
+
+    protected static enum NumParameters {
+        ZERO, ONE, UNLIMITED;
+    }
+
+    private final Type type;
+    private final NumParameters numParameters;
+
+    public PerformAbstractTypeParams(final String key, final Type type, final NumParameters numParameters, final Perform.Mode mode) {
+        super(key, mode);
+        this.type = type;
+        this.numParameters = numParameters;
+    }
+
+    @Override
+    public boolean requiresMember() {
+        return type.representsMember();
+    }
+
+    /**
+     * Can be overridden, but provides basic checking that member is of correct
+     * type and, if taking {@link NumParameters#ZERO zero} or
+     * {@link NumParameters#ONE one} parameter, that the correct number of
+     * actual arguments match.
+     */
+    @Override
+    public void perform(final PerformContext performContext) throws ScenarioBoundValueException {
+
+        final CellBinding onMemberBinding = performContext.getPeer().getOnMemberBinding();
+        final ScenarioCell onMemberCell = onMemberBinding.getCurrentCell();
+
+        final String reason = type.ensureMemberIsOfType(performContext.getObjectMember());
+        if (reason != null) {
+            throw ScenarioBoundValueException.current(onMemberBinding, reason);
+        }
+
+        if (type.representsMember()) {
+
+            if (getMode() == Perform.Mode.TEST) {
+
+                performContext.ensureAvailableForDeploymentType(onMemberBinding, onMemberCell);
+
+                performContext.ensureVisible(onMemberBinding, onMemberCell);
+                performContext.ensureUsable(onMemberBinding, onMemberCell);
+            }
+
+            // validate we have correct number of parameters if zero or one.
+            final int numArgs = performContext.getArgumentCells().size();
+
+            // don't do these two checks, because there could be additional
+            // cells due to previous or subsequent rows in the table
+            // (with the author keeping this 'tidy')
+
+            // if (numParameters == NumParameters.ZERO && length > 0) {
+            // ... "(no arguments required)"
+            // }
+            // if (numParameters == NumParameters.ONE && length > 1) {
+            // ... "(need just 1 argument)"
+            // }
+
+            // okay to do this check, though
+            if (numParameters == NumParameters.ONE && numArgs < 1) {
+                throw ScenarioBoundValueException.current(onMemberBinding, "(need one argument)");
+            }
+        }
+
+        doHandle(performContext);
+    }
+
+    /**
+     * Hook method that does nothing; should be overridden if
+     * {@link #perform(PerformContextForFitNesse) handle(...)} method has not
+     * been.
+     */
+    protected void doHandle(final PerformContext performContext) throws ScenarioBoundValueException {
+        // does nothing
+    }
+
+    // /**
+    // * Convenience; delegates to
+    // * {@link Type#ensureMemberIsOfType(NakedObjectMember)} and downcasts.
+    // */
+    // protected OneToOneAssociation ensureMemberIsProperty(
+    // final NakedObjectMember nakedObjectMember,
+    // final StoryCell usingMemberCell) throws StoryFailureException {
+    // type.ensureMemberIsOfType(nakedObjectMember, usingMemberCell);
+    // return (OneToOneAssociation) nakedObjectMember;
+    // }
+    //
+    // /**
+    // * Convenience; delegates to
+    // * {@link Type#ensureMemberIsOfType(NakedObjectMember)} and downcasts.
+    // */
+    // protected OneToManyAssociation ensureMemberIsCollection(
+    // final NakedObjectMember nakedObjectMember,
+    // final StoryCell usingMemberCell) throws StoryFailureException {
+    // type.ensureMemberIsOfType(nakedObjectMember, usingMemberCell);
+    // return (OneToManyAssociation) nakedObjectMember;
+    // }
+    //
+    // /**
+    // * Convenience; delegates to
+    // * {@link Type#ensureMemberIsOfType(NakedObjectMember)} and downcasts.
+    // */
+    // protected NakedObjectAction ensureMemberIsAction(
+    // final NakedObjectMember nakedObjectMember,
+    // final StoryCell usingMemberCell) throws StoryFailureException {
+    // type.ensureMemberIsOfType(nakedObjectMember, usingMemberCell);
+    // return (NakedObjectAction) nakedObjectMember;
+    // }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformContext.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformContext.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformContext.java
new file mode 100644
index 0000000..435b36a
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformContext.java
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import java.util.List;
+
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.core.commons.authentication.AuthenticationSession;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.facets.actions.exploration.ExplorationFacet;
+import org.apache.isis.core.metamodel.facets.actions.prototype.PrototypeFacet;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.runtimes.dflt.runtime.system.DeploymentType;
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.ScenarioBoundValueException;
+import org.apache.isis.viewer.bdd.common.ScenarioCell;
+import org.apache.isis.viewer.bdd.common.fixtures.UsingIsisViewerPeer;
+import org.apache.isis.viewer.bdd.common.parsers.DateParser;
+
+/**
+ * Represents the context for a single {@link Perform} command; in effect, a row
+ * of a table.
+ */
+public class PerformContext {
+
+    // REVIEW: should provide this rendering context, rather than hardcoding.
+    // the net effect currently is that class members annotated with 
+    // @Hidden(where=Where.ANYWHERE) or @Disabled(where=Where.ANYWHERE) will indeed
+    // be hidden/disabled, but will be visible/enabled (perhaps incorrectly) 
+    // for any other value for Where
+    private final Where where = Where.ANYWHERE;
+
+    private final UsingIsisViewerPeer peer;
+
+    private final ObjectAdapter onAdapter;
+    private final ObjectMember objectMember;
+    private final List<ScenarioCell> argumentCells;
+
+    public PerformContext(final UsingIsisViewerPeer peer, final ObjectAdapter onAdapter, final ObjectMember objectMember, final List<ScenarioCell> argumentCells) {
+        this.onAdapter = onAdapter;
+        this.objectMember = objectMember;
+        this.peer = peer;
+        this.argumentCells = argumentCells;
+    }
+
+    public UsingIsisViewerPeer getPeer() {
+        return peer;
+    }
+
+    public ObjectAdapter getOnAdapter() {
+        return onAdapter;
+    }
+
+    public ObjectMember getObjectMember() {
+        return objectMember;
+    }
+
+    public List<ScenarioCell> getArgumentCells() {
+        return argumentCells;
+    }
+
+    public Consent visibleMemberConsent() {
+        return getObjectMember().isVisible(getAuthenticationSession(), getOnAdapter(), where);
+    }
+
+    public Consent usableMemberConsent() {
+        return getObjectMember().isUsable(getAuthenticationSession(), getOnAdapter(), where);
+    }
+
+    public Consent validObjectConsent() {
+        final ObjectAdapter onAdapter = getOnAdapter();
+        return onAdapter.getSpecification().isValid(onAdapter);
+    }
+
+    public void ensureVisible(final CellBinding onMemberBinding, final ScenarioCell onMemberCell) throws ScenarioBoundValueException {
+        final Consent visible = objectMember.isVisible(getAuthenticationSession(), getOnAdapter(), where);
+        if (visible.isVetoed()) {
+            throw ScenarioBoundValueException.current(onMemberBinding, "(not visible)");
+        }
+    }
+
+    public void ensureUsable(final CellBinding onMemberBinding, final ScenarioCell onMemberCell) throws ScenarioBoundValueException {
+        final Consent usable = objectMember.isUsable(getAuthenticationSession(), getOnAdapter(), where);
+        if (usable.isVetoed()) {
+            throw ScenarioBoundValueException.current(onMemberBinding, "(not usable)");
+        }
+    }
+
+    public void ensureAvailableForDeploymentType(final CellBinding onMemberBinding, final ScenarioCell onMemberCell) throws ScenarioBoundValueException {
+        final DeploymentType deploymentType = this.peer.getDeploymentType();
+
+        final boolean isExploration = objectMember.getFacet(ExplorationFacet.class) != null;
+        if (isExploration && !deploymentType.isExploring()) {
+            throw ScenarioBoundValueException.current(onMemberBinding, "(not running in exploration mode)");
+        }
+
+        final boolean isPrototype = objectMember.getFacet(PrototypeFacet.class) != null;
+        if (isPrototype && !deploymentType.isPrototyping()) {
+            throw ScenarioBoundValueException.current(onMemberBinding, "(not running in prototype mode)");
+        }
+    }
+
+    protected AuthenticationSession getAuthenticationSession() {
+        return getPeer().getAuthenticationSession();
+    }
+
+    public DateParser getDateParser() {
+        return peer.getDateParser();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformOwner.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformOwner.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformOwner.java
new file mode 100644
index 0000000..06b5157
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/PerformOwner.java
@@ -0,0 +1,23 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+public interface PerformOwner {
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/255ef514/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/RemoveFromCollection.java
----------------------------------------------------------------------
diff --git a/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/RemoveFromCollection.java b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/RemoveFromCollection.java
new file mode 100644
index 0000000..7fe0b39
--- /dev/null
+++ b/component/viewer/bdd/common/src/main/java/org/apache/isis/viewer/bdd/common/fixtures/perform/RemoveFromCollection.java
@@ -0,0 +1,86 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.bdd.common.fixtures.perform;
+
+import java.util.List;
+
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.consent.InteractionInvocationMethod;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionRemoveFromFacet;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.viewer.bdd.common.CellBinding;
+import org.apache.isis.viewer.bdd.common.ScenarioBoundValueException;
+import org.apache.isis.viewer.bdd.common.ScenarioCell;
+
+public class RemoveFromCollection extends PerformAbstractTypeParams {
+
+    private ObjectAdapter result;
+
+    public RemoveFromCollection(final Perform.Mode mode) {
+        super("remove from collection", Type.COLLECTION, NumParameters.ONE, mode);
+    }
+
+    @Override
+    public void doHandle(final PerformContext performContext) throws ScenarioBoundValueException {
+
+        final ObjectAdapter onAdapter = performContext.getOnAdapter();
+        final ObjectMember nakedObjectMember = performContext.getObjectMember();
+        final CellBinding onMemberBinding = performContext.getPeer().getOnMemberBinding();
+        @SuppressWarnings("unused")
+        final ScenarioCell onMemberCell = onMemberBinding.getCurrentCell();
+
+        final List<ScenarioCell> argumentCells = performContext.getArgumentCells();
+
+        final OneToManyAssociation otma = (OneToManyAssociation) nakedObjectMember;
+
+        // safe since guaranteed by superclass
+        final CellBinding arg0Binding = performContext.getPeer().getArg0Binding();
+        final ScenarioCell arg0Cell = argumentCells.get(0);
+        final String toRemove = arg0Cell.getText();
+
+        final CollectionRemoveFromFacet removeFromFacet = nakedObjectMember.getFacet(CollectionRemoveFromFacet.class);
+        if (removeFromFacet == null) {
+            throw ScenarioBoundValueException.current(onMemberBinding, "(cannot remove from collection)");
+        }
+
+        final ObjectAdapter toRemoveAdapter = performContext.getPeer().getAliasRegistry().getAliased(toRemove);
+        if (toRemoveAdapter == null) {
+            throw ScenarioBoundValueException.current(arg0Binding, "(unknown alias)");
+        }
+
+        // validate argument
+        otma.createValidateAddInteractionContext(getSession(), InteractionInvocationMethod.BY_USER, onAdapter, toRemoveAdapter);
+        final Consent validToRemove = otma.isValidToRemove(onAdapter, toRemoveAdapter);
+        if (validToRemove.isVetoed()) {
+            throw ScenarioBoundValueException.current(onMemberBinding, validToRemove.getReason());
+        }
+
+        // remove
+        removeFromFacet.remove(onAdapter, toRemoveAdapter);
+
+    }
+
+    @Override
+    public ObjectAdapter getResult() {
+        return result;
+    }
+
+}