/*******************************************************************************
* CogTool Copyright Notice and Distribution Terms
* CogTool 1.3, Copyright (c) 2005-2013 Carnegie Mellon University
* This software is distributed under the terms of the FSF Lesser
* Gnu Public License (see LGPL.txt).
*
* CogTool is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* CogTool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CogTool; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* CogTool makes use of several third-party components, with the
* following notices:
*
* Eclipse SWT version 3.448
* Eclipse GEF Draw2D version 3.2.1
*
* Unless otherwise indicated, all Content made available by the Eclipse
* Foundation is provided to you under the terms and conditions of the Eclipse
* Public License Version 1.0 ("EPL"). A copy of the EPL is provided with this
* Content and is also available at http://www.eclipse.org/legal/epl-v10.html.
*
* CLISP version 2.38
*
* Copyright (c) Sam Steingold, Bruno Haible 2001-2006
* This software is distributed under the terms of the FSF Gnu Public License.
* See COPYRIGHT file in clisp installation folder for more information.
*
* ACT-R 6.0
*
* Copyright (c) 1998-2007 Dan Bothell, Mike Byrne, Christian Lebiere &
* John R Anderson.
* This software is distributed under the terms of the FSF Lesser
* Gnu Public License (see LGPL.txt).
*
* Apache Jakarta Commons-Lang 2.1
*
* This product contains software developed by the Apache Software Foundation
* (http://www.apache.org/)
*
* jopt-simple version 1.0
*
* Copyright (c) 2004-2013 Paul R. Holser, Jr.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Mozilla XULRunner 1.9.0.5
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (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.mozilla.org/MPL/.
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The J2SE(TM) Java Runtime Environment version 5.0
*
* Copyright 2009 Sun Microsystems, Inc., 4150
* Network Circle, Santa Clara, California 95054, U.S.A. All
* rights reserved. U.S.
* See the LICENSE file in the jre folder for more information.
******************************************************************************/
package edu.cmu.cs.hcii.cogtool.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EventObject;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import edu.cmu.cs.hcii.cogtool.util.GlobalAttributed;
import edu.cmu.cs.hcii.cogtool.util.ObjectLoader;
import edu.cmu.cs.hcii.cogtool.util.ObjectSaver;
/**
* Script object contains a start frame and sequence of DefaultModelGeneratorState
* instances, which refer to the ScriptSteps that perform a Task on a specific
* Design.
*
* @author alexeiser
*/
public class Script extends GlobalAttributed
{
public static final int edu_cmu_cs_hcii_cogtool_model_Script_version = 3;
// For loading instances of versions 0 and 1:
protected static final String scriptStepsVAR = "scriptSteps";
// For version 2
protected static final String demonstrationVAR = "demonstration";
protected static final String modelGenVAR = "modelGen";
protected static final String stepStatesVAR = "stepStates";
// for version 3
protected static final String externalPathVAR = "externalPath";
/**
* The demonstration with which this script is associated
*/
protected Demonstration demonstration;
/**
* A list of DefaultModelGeneratorState instances, which hold the IScriptSteps
* that define the transitions needed to perform a task.
*/
protected List<DefaultModelGeneratorState> stepStates =
new ArrayList<DefaultModelGeneratorState>();
/**
* The algorithm to use for script step generation.
*/
protected CognitiveModelGenerator modelGen;
/**
* Reference to an external path used by some algorithms
*/
protected String externalPath = null;
/**
* Saver object which is responsible saving Script model objects
*/
private static ObjectSaver.IDataSaver<Script> SAVER =
new ObjectSaver.ADataSaver<Script>() {
@Override
public int getVersion()
{
return edu_cmu_cs_hcii_cogtool_model_Script_version;
}
@Override
public void saveData(Script v, ObjectSaver saver)
throws java.io.IOException
{
// Store the design start frame and the set of script steps.
saver.saveObject(v.demonstration, demonstrationVAR);
saver.saveObject(v.modelGen, modelGenVAR);
saver.saveObject(v.stepStates, stepStatesVAR);
saver.saveString(v.externalPath, externalPathVAR);
}
};
/**
* Register function to add the script class and the saver object to a
* central registration
*
*/
public static void registerSaver()
{
ObjectSaver.registerSaver(Script.class.getName(), SAVER);
}
/**
* The objects for handling loading of scripts.
*/
private static class ScriptLoader_v1 extends ObjectLoader.AObjectLoader<Script>
{
private static ObjectLoader.IAggregateLoader scriptStepLoader =
new ObjectLoader.AAggregateLoader() {
@Override
public <T> void addToCollection(ObjectLoader l,
Collection<? super T> c,
T v)
{
// IGNORE; the step is no longer added to the script.
// Creation of an DefaultModelGeneratorState (see
// KLMCognitiveGenerator's IScriptStepState_LOADER)
// will add the corresponding state to the enclosing
// script's DefaultModelGeneratorState sequence. The
// script-state relationship is inverted in
// AScriptStep's LOADER_v1 when it is attempting
// to assign the state to the script step.
}
};
protected void setStartFrame(ObjectLoader l, Frame startFrameValue)
{
TaskApplication ta = l.getPendingObject(TaskApplication.class);
if (ta != null) {
if ((startFrameValue != null) &&
(startFrameValue.getDesign() == null))
{
startFrameValue.setDesign(ta.getDesign());
}
Demonstration demo = ta.getDemonstration();
demo.setStartFrame(startFrameValue);
if (startFrameValue != null) {
demo.setStartFrameChosen(true);
}
}
else {
throw new IllegalStateException("Missing context TaskApplication");
}
}
protected void setHandLocation(ObjectLoader l,
HandLocation handLoc)
{
TaskApplication ta = l.getPendingObject(TaskApplication.class);
if (ta != null) {
DefaultModelGeneratorState state =
ta.getDemonstration().getInitialState();
state.setHandLocation(HandLocation.RIGHT_HAND, handLoc);
}
else {
throw new IllegalStateException("Missing context TaskApplication");
}
}
@Override
public Script createObject(ObjectLoader l)
{
TaskApplication ta = l.getPendingObject(TaskApplication.class);
if (ta != null) {
return new Script(ta.getDemonstration(),
KLMCognitiveGenerator.ONLY);
}
throw new IllegalStateException("Missing context TaskApplication");
}
/**
* Set object that handles object values.
* In this case, the design and the start frame
*/
@Override
public void set(ObjectLoader l,
Script target, String variable, Object value)
{
// For versions 0 and 1:
final String startFrameVAR = "startFrame";
if (variable != null) {
// For versions 2 and up, we ignore variable designVAR
if (variable.equals(startFrameVAR)) {
setStartFrame(l, (Frame) value);
}
}
}
@Override
public ObjectLoader.IAggregateLoader getLoader(String variable)
{
if (variable != null) {
if (variable.equals(scriptStepsVAR)) {
return scriptStepLoader;
}
}
return super.getLoader(variable);
}
/**
* Collection creation utility for ScriptStep list.
*/
@Override
public Collection<?> createCollection(Script target,
String variable,
int size)
{
if (variable != null) {
if (variable.equals(scriptStepsVAR)) {
// Pretend that this is really stepStates;
// the inversion (states referring to steps instead
// of steps referring to states) occurs in
// the loader returned by getLoader above.
return target.stepStates;
}
}
return null;
}
@Override
public void set(ObjectLoader l,
Script target, String variable, boolean value)
{
final String handStartsOnMouseVAR = "handStartsOnMouse";
if (variable != null) {
if (variable.equals(handStartsOnMouseVAR)) {
setHandLocation(l, value ? HandLocation.OnMouse
: HandLocation.OnKeyboard);
}
}
}
// Must make a sweep through Script to set owner properly for others
// and collect the steps actually inserted by the user into the
// Script's demonstration.
@Override
public void evolve(Script target)
{
Demonstration demo = target.getDemonstration();
TaskApplication ta = demo.getTaskApplication();
Design design = (ta != null) ? ta.getDesign() : null;
List<DefaultModelGeneratorState> stepStates = target.getStepStates();
List<AScriptStep> demoSteps = demo.getSteps();
AScriptStep lastOwner = null;
// Go backwards so we can set owner properly
for (int i = stepStates.size() - 1; i >= 0; i--) {
DefaultModelGeneratorState state = stepStates.get(i);
AScriptStep step = state.getScriptStep();
Frame currentFrame = step.getCurrentFrame();
Frame destinationFrame = step.getDestinationFrame();
if (currentFrame.getDesign() == null) {
currentFrame.setDesign(design);
}
if (destinationFrame.getDesign() == null) {
destinationFrame.setDesign(design);
}
if (step.isInsertedByUser()) {
if (step.isInvalid()) {
demo.noteEdit(DemonstrationState.INVALIDATING);
}
// TextActionSegment instances are never owners
if (step instanceof TextActionSegment) {
TextActionSegment textSeg = (TextActionSegment) step;
step = textSeg.getOwner();
if (textSeg.isInvalid()) {
step.noteEdit(DemonstrationState.INVALIDATING);
textSeg.revertEdit(textSeg,
DemonstrationState.INVALIDATING);
}
}
demoSteps.add(0, step);
lastOwner = step;
}
else {
step.setOwner(lastOwner);
}
}
// Make a forward sweep to reset states' last moved-to property.
DefaultModelGeneratorState state = demo.getInitialState();
IWidget lastMovedToWidget = state.getLastMovedToWidget();
IWidget lastClickedWidget = state.getLastClickedWidget();
for (int i = 0; i < stepStates.size(); i++) {
state = stepStates.get(i);
// If the lastClickedWidget changes, then change the
// lastMovedToWidget as well since, in b18,
// one could not generate a stand-alone move-mouse
// (it is always generated from a mouse click, tap, hover,
// or Graffiti gesture), it should be ok to tie it to
// lastClickedWidget if it is different.
IWidget nextLastClickedWidget = state.getLastClickedWidget();
if (lastClickedWidget != nextLastClickedWidget) {
lastMovedToWidget = nextLastClickedWidget;
}
else {
// Check for explicit, generated moves
AScriptStep step = state.getScriptStep();
if (step instanceof ActionScriptStep) {
ActionScriptStep actionStep = (ActionScriptStep) step;
AAction stepAction = actionStep.getAction();
if (stepAction instanceof MoveMouseAction) {
TransitionSource stepFocus =
actionStep.getStepFocus();
if (stepFocus instanceof IWidget) {
lastMovedToWidget = (IWidget) stepFocus;
}
}
}
}
state.setLastMovedToWidget(lastMovedToWidget);
}
}
}
/**
* Inform observers of changes to the list of contained script step states.
* Handles both singleton and multiple state changes; if
* <code>stepState</code> is <code>null</code>, then the change
* is "multiple" and <code>stepStates</code> should be used.
*/
public static class StepStateChange extends EventObject
{
public static final int ADD_STATE = 0;
public static final int REMOVE_STATE = 1;
/**
* The index most relevent to the change.
* Can be the item changed/added/deleted or the last item in a
* multiple item change.
*/
public int index;
/**
* The single script item which caused the change.
*/
public DefaultModelGeneratorState stepState;
/**
* For a multiple item change, this includes all the changed items.
*/
public List<DefaultModelGeneratorState> stepStates;
/**
* The action type being performed.
* Should be a value from the list above.
*/
public int action;
/**
* Constructor for a singleton step state change.
* Provides the source, step state, the index and the action taken.
*
* Throws an exception if the action is invalid for this constructor
* @param source
* @param changed
* @param indx
* @param chgAction
*/
public StepStateChange(Script s,
DefaultModelGeneratorState changed,
int indx,
int chgAction)
{
super(s);
stepState = changed;
stepStates = null;
index = indx;
action = chgAction;
}
/**
* Constructor for a multiple step state change.
* Provides the source, step states, the index and the action taken.
*
* Throws an exception if the action is invalid for this constructor
* @param source
* @param changed
* @param indx
* @param chgAction
*/
public StepStateChange(Script s,
List<DefaultModelGeneratorState> changed,
int indx,
int chgAction)
{
super(s);
stepState = null;
stepStates = changed;
index = indx;
action = chgAction;
}
}
private static ObjectLoader.IObjectLoader<Script> LOADER_v1 = new ScriptLoader_v1();
private static ObjectLoader.IObjectLoader<Script> LOADER =
new ObjectLoader.AObjectLoader<Script>() {
@Override
public Script createObject()
{
return new Script();
}
/**
* Set object that handles object values.
*/
@Override
public void set(Script target, String variable, Object value)
{
if (variable != null) {
if (variable.equals(demonstrationVAR)) {
target.demonstration = (Demonstration) value;
}
else if (variable.equals(modelGenVAR)) {
target.modelGen = (CognitiveModelGenerator) value;
}
else if (variable.equals(externalPathVAR)) {
target.externalPath = (String) value;
}
}
}
/**
* Collection creation utility for ScriptStepState list.
*/
@Override
public Collection<?> createCollection(Script target,
String variable,
int size)
{
if (variable != null) {
if (variable.equals(stepStatesVAR)) {
return target.stepStates;
}
}
return null;
}
};
/**
* Function to register the object so it can be loaded correctly.
*/
public static void registerLoader()
{
// Use the v1 loader for revision 0; the default value of
// hand-on-mouse is sufficient.
ObjectLoader.registerLoader(Script.class.getName(),
0,
LOADER_v1);
ObjectLoader.registerLoader(Script.class.getName(),
1,
LOADER_v1);
ObjectLoader.registerLoader(Script.class.getName(),
2,
LOADER);
ObjectLoader.registerLoader(Script.class.getName(),
edu_cmu_cs_hcii_cogtool_model_Script_version,
LOADER);
}
public static ObjectLoader.IAggregateLoader fetchCurrentStateLoader()
{
return LOADER.getLoader(stepStatesVAR);
}
/**
* The constructor for a script, needing the demonstration this
* is a script for and the model generation algorithm used to
* generate the steps.
*/
public Script(Demonstration demo, CognitiveModelGenerator gen)
{
demonstration = demo;
modelGen = gen;
if (demo.isStartFrameChosen()) {
gen.generateInitialSteps(demo.getStartFrame(),
demo.getInitialState(),
null,
stepStates);
}
}
/**
* Zero-argument constructor for use by loading
*/
protected Script() { }
/**
* Get the demonstration used for this script.
* @return
*/
public Demonstration getDemonstration()
{
return demonstration;
}
/**
* Reset associated Demonstration
* @param d
*/
public void setDemonstration(Demonstration d)
{
demonstration = d;
}
/**
* Get the model generator that was used to generate this script.
* @return
*/
public CognitiveModelGenerator getModelGenerator()
{
return modelGen;
}
/**
* Return the number of step states in the script.
*/
public int getStepStateCount()
{
return stepStates.size();
}
/**
* List of IModelGeneratorStates which make up this Script.
*
* @return list of IModelGeneratorStates
*/
public List<DefaultModelGeneratorState> getStepStates()
{
return stepStates;
}
/**
* Fetch the DefaultModelGeneratorState instance at the given index.
*/
public DefaultModelGeneratorState getStepState(int atIndex)
{
return stepStates.get(atIndex);
}
/**
* Fetch the index of the DefaultModelGeneratorState corresponding to the given
* demonstrated AScriptStep. The <code>null</code> step represents the end
* of the list.
*/
public int getStepStateIndex(AScriptStep demoStep)
{
int atIndex = stepStates.size();
while (demoStep != null) {
int stepStateIndex = -1;
while (atIndex > 0) {
DefaultModelGeneratorState state = stepStates.get(--atIndex);
AScriptStep scriptStep = state.getScriptStep();
if (scriptStep.getOwner() == demoStep) {
if (scriptStep.isInsertedByUser()) {
return atIndex;
}
// If the list contains a step state owned by demoStep
// but no step state owned by demoStep is in the list that
// isInsertedByUser, return the largest owned step's index.
if (stepStateIndex < 0) {
stepStateIndex = atIndex;
}
}
}
// We got here because the list did not contain a step state
// that is owned by demoStep and isInsertedByUser. If the list
// does contain a step state owned by demoStep, return the largest
// such index.
if (0 <= stepStateIndex) {
return stepStateIndex;
}
demoStep = demonstration.getNextStep(demoStep);
atIndex = stepStates.size() - 1;
// throw new IllegalArgumentException("Given step is not part of this script");
}
return atIndex;
}
/**
* Fetch the DefaultModelGeneratorState that precedes all steps inserted for the
* demonstrated owner AScriptStep. If the given step is <code>null</code>,
* return the Script's last state. If the given step is the first
* demonstrated step, then <code>null</code> is returned.
*/
public DefaultModelGeneratorState getPreviousState(AScriptStep demoStep)
{
if (demoStep == null) {
return getLastState();
}
int atIndex = stepStates.size();
boolean foundDemoStep = false;
while (atIndex > 0) {
DefaultModelGeneratorState state = stepStates.get(--atIndex);
AScriptStep owningStep = state.getScriptStep().getOwner();
if (foundDemoStep) {
if (demoStep != owningStep) {
// Done!
return state;
}
}
else {
if (demoStep == owningStep) {
foundDemoStep = true;
}
}
}
return null; // forStep must have been the first owner step!
}
/**
* Fetch the DefaultModelGeneratorState preceding the given one.
* If the given state is <code>null</code>, returns the Script's last state.
* If the given state is the first one, <code>null</code> is returned.
*/
public DefaultModelGeneratorState getPreviousState(DefaultModelGeneratorState state)
{
if (state == null) {
return getLastState();
}
int atIndex = stepStates.indexOf(state);
if (atIndex > 0) {
return stepStates.get(atIndex - 1);
}
return null;
}
/**
* Fetch the last DefaultModelGeneratorState; if no step states, return null.
* If the initial state from the script's demonstration is desired,
* the caller should detect that case and request it.
*/
public DefaultModelGeneratorState getLastState()
{
int size = stepStates.size();
if (size > 0) {
return stepStates.get(size - 1);
}
return null;
}
/**
* Inseart the given step state at the given index.
*/
public void insertState(DefaultModelGeneratorState state, int atIndex)
{
if (state == null) {
throw new IllegalArgumentException("State to insert may not be null");
}
stepStates.add(atIndex, state);
raiseAlert(new Script.StepStateChange(this, state, atIndex,
Script.StepStateChange.ADD_STATE));
}
/**
* Delete given step state, returning its index
*/
public int removeState(DefaultModelGeneratorState state)
{
if (state == null) {
throw new IllegalArgumentException("State to remove may not be null");
}
int atIndex = stepStates.indexOf(state);
if (atIndex == -1) {
throw new IllegalStateException("State to remove must be in the list");
}
removeState(atIndex);
raiseAlert(new Script.StepStateChange(this, state, atIndex,
Script.StepStateChange.REMOVE_STATE));
return atIndex;
}
/**
* Remove step at the given index.
*/
public void removeState(int atIndex)
{
stepStates.remove(atIndex);
}
/**
* Utility to help both removeStepStates and replaceStepStates
* so that the alert gets raised only once.
*/
protected int removeStates(AScriptStep oldDemoStep,
List<DefaultModelGeneratorState> removedStepStates)
{
int atIndex = stepStates.size();
ListIterator<DefaultModelGeneratorState> stepStatesRev;
if (oldDemoStep != null) {
boolean foundDemoStep = false;
stepStatesRev = stepStates.listIterator(atIndex);
while (stepStatesRev.hasPrevious()) {
DefaultModelGeneratorState state = stepStatesRev.previous();
if (foundDemoStep) {
if (oldDemoStep != state.getScriptStep().getOwner()) {
// Done!
break;
}
}
else {
if (oldDemoStep == state.getScriptStep().getOwner()) {
foundDemoStep = true;
}
}
removedStepStates.add(0, state);
stepStatesRev.remove();
atIndex--;
}
if (! foundDemoStep) {
throw new IllegalStateException("Did not find oldDemoStep when removing script step states.");
}
}
stepStatesRev = stepStates.listIterator(atIndex);
// "Remove" all initial state steps, if they come next
while (stepStatesRev.hasPrevious()) {
DefaultModelGeneratorState state = stepStatesRev.previous();
if (state.getScriptStep().isInitiallyGenerated()) {
removedStepStates.add(0, state);
stepStatesRev.remove();
atIndex--;
}
else {
break;
}
}
return atIndex;
} // removeStates
/**
* Removes all the step states corresponding to the given demonstration
* step and all subsequent step states. Enters each replaced step state
* into the given removedStepStates. Returns the index of the earliest
* step state. If the oldDemoStep is <code>null</code>, the size of the
* step state list is returned.
*/
public int removeStepStates(AScriptStep oldDemoStep,
List<DefaultModelGeneratorState> removedStepStates)
{
int atIndex = removeStates(oldDemoStep, removedStepStates);
raiseAlert(new Script.StepStateChange(this, removedStepStates, atIndex,
Script.StepStateChange.REMOVE_STATE));
return atIndex;
}
/**
* Replaces all the step states corresponding to the given demonstration
* step and all subsequent step states and then appends the given list of
* step states (newStepStates). Enters each replaced step state into the
* given removedStepStates. Returns the index of the earliest step state.
* If the oldDemoStep is <code>null</code>, the size of the
* step state list is returned.
*/
public int replaceStepStates(AScriptStep oldDemoStep,
List<DefaultModelGeneratorState> newStepStates,
List<DefaultModelGeneratorState> removedStepStates)
{
int atIndex = removeStates(oldDemoStep, removedStepStates);
replaceStepStates(atIndex, newStepStates);
return atIndex;
}
/**
* Replaces all the step states starting at the given index with the
* given list of step states. Suitable for implementing undo/redo.
*/
public void replaceStepStates(int atIndex, List<DefaultModelGeneratorState> newStepStates)
{
int deleteIndex = stepStates.size();
while (deleteIndex > atIndex) {
stepStates.remove(--deleteIndex);
}
stepStates.addAll(newStepStates);
raiseAlert(new Script.StepStateChange(this, newStepStates, atIndex,
Script.StepStateChange.ADD_STATE));
}
/**
* Create a "deep" copy of this script.
* <p>
* It is the responsibility of the caller to "place" the copy
* (usually by adding it to an TaskApplication).
*
* @return the script copy
* @author mlh
*/
public Script duplicate(final Demonstration demo,
CognitiveModelGenerator modelGenerator,
TaskApplication.DemoDuplicateScope duplicateScope)
{
AScriptStep.GeneratedStepDuplicateScope ownerScope =
new AScriptStep.GeneratedStepDuplicateScope()
{
protected Iterator<AScriptStep> demoSteps =
demo.getSteps().iterator();
protected AScriptStep lastOriginalOwnerStep = null;
protected AScriptStep lastCopyOwnerStep = null;
public AScriptStep getOwner(AScriptStep originalOwner)
{
if (lastOriginalOwnerStep != originalOwner) {
lastOriginalOwnerStep = originalOwner;
if (! demoSteps.hasNext()) {
return null;
}
lastCopyOwnerStep = demoSteps.next();
}
return lastCopyOwnerStep;
}
};
Script copy = new Script(demo, modelGenerator);
int numSteps = stepStates.size();
List<DefaultModelGeneratorState> copyStates = copy.getStepStates();
for (int i = 0; i < numSteps; i++) {
DefaultModelGeneratorState stateToCopy = stepStates.get(i);
copyStates.add(stateToCopy.duplicate(duplicateScope, ownerScope));
}
if (getAssociatedPath() != null) {
copy.setAssociatedPath(new String(getAssociatedPath()));
}
copy.copyAttributes(this);
return copy;
}
/**
* Return a list iterator returning the DefaultModelGeneratorState instances
* starting with the one at the given index.
*/
public ListIterator<DefaultModelGeneratorState> getStepStatesAt(DefaultModelGeneratorState state)
{
int atIndex = stepStates.indexOf(state);
if (atIndex == -1) {
throw new IllegalStateException("Given state is not in the list");
}
return stepStates.listIterator(atIndex + 1);
}
public String getAssociatedPath()
{
if (externalPath != null) {
return externalPath;
}
return getDemonstration().getTaskApplication().getDefaultAssociatedPath();
}
public void setAssociatedPath(String path)
{
externalPath = path;
}
}