// Copyright 2012 Georg-August-Universität Göttingen, Germany // // Licensed 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 de.ugoe.cs.autoquest.tasktrees; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import de.ugoe.cs.autoquest.eventcore.Event; import de.ugoe.cs.autoquest.eventcore.IEventTarget; import de.ugoe.cs.autoquest.eventcore.IEventType; import de.ugoe.cs.autoquest.eventcore.StringEventType; import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction; import de.ugoe.cs.autoquest.eventcore.gui.MouseClick; import de.ugoe.cs.autoquest.eventcore.gui.Scroll; import de.ugoe.cs.autoquest.eventcore.gui.TextInput; import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask; import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration; import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptional; import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptionalInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskBuilder; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskFactory; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstanceList; import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession; import de.ugoe.cs.autoquest.test.DummyGUIElement; import de.ugoe.cs.autoquest.test.DummyTextField; /** * TODO comment * * @version $Revision: $ $Date: 01.04.2012$ * @author 2012, last modified by $Author: patrick$ */ public class TaskTreeDecoder { /** */ private static Pattern taskInstancePattern = Pattern.compile("([^{}]+)\\{|\\}"); /** */ private static Pattern taskInstanceDetailsPattern = Pattern.compile("\\s*(\\w*)\\s*([\\w\\(\\)\"]*)\\s*(\\(([\\w*\\s*]*)\\))?((\\w*)|(\".*\"))?"); /** */ private ITaskFactory taskFactory; /** */ private ITaskBuilder taskBuilder; /** */ Map targets = new HashMap(); /** */ Map tasks = new HashMap(); /** * */ public TaskTreeDecoder(ITaskFactory taskFactory, ITaskBuilder taskBuilder) { super(); this.taskFactory = taskFactory; this.taskBuilder = taskBuilder; } /** * */ public ITaskInstanceList decode(String taskTreeSpec) { ITaskInstanceList taskInstanceList = null; Matcher taskMatcher = taskInstancePattern.matcher(taskTreeSpec); if (taskMatcher.find()) { taskInstanceList = parseTaskInstanceList(taskMatcher); } if (taskMatcher.find()) { throw new IllegalArgumentException("too many tasks specified"); } correctTargets(taskInstanceList); // validate the instance list try { new TaskTreeValidator().validate(taskInstanceList); } catch (java.lang.AssertionError e) { throw new IllegalArgumentException(e.getMessage(), e); } return taskInstanceList; } /** * */ private ITaskInstanceList parseTaskInstanceList(Matcher taskMatcher) { if ("}".equals(taskMatcher.group(1))) { throw new IllegalArgumentException("invalid task instance list specification"); } String taskDetails = taskMatcher.group(1); Matcher matcher = taskInstanceDetailsPattern.matcher(taskDetails); if (!matcher.find()) { throw new IllegalArgumentException("could not parse task details"); } String type = matcher.group(1); ITaskInstanceList list; if ("UserSession".equals(type)) { list = taskFactory.createUserSession(); } else if ("TaskInstances".equals(type)) { list = taskFactory.createNewTaskInstance(taskFactory.createNewSequence()); } else { throw new IllegalArgumentException("unknown type of task instance list: " + type); } while (taskMatcher.find() && !"}".equals(taskMatcher.group(0))) { ITaskInstance childInstance = parseTaskInstance(taskMatcher); if (!(list instanceof IUserSession)) { taskBuilder.addChild ((ISequence) ((ITaskInstance) list).getTask(), childInstance.getTask()); } taskBuilder.addTaskInstance(list, childInstance); } return list; } /** * */ private ITaskInstance parseTaskInstance(Matcher taskMatcher) { if ("}".equals(taskMatcher.group(1))) { throw new IllegalArgumentException("invalid task instance specification"); } String taskDetails = taskMatcher.group(1); Matcher matcher = taskInstanceDetailsPattern.matcher(taskDetails); if (!matcher.find()) { throw new IllegalArgumentException("could not parse task details"); } String type = matcher.group(1); String id = matcher.group(2); // determine the children before creating this instance List children = new LinkedList(); while (taskMatcher.find() && !"}".equals(taskMatcher.group(0))) { children.add(parseTaskInstance(taskMatcher)); } // get the model ITask task = getCreateTask(id, type, children); // create the respective instance ITaskInstance instance; if (task instanceof ISequence) { instance = taskFactory.createNewTaskInstance((ISequence) task); } else if (task instanceof ISelection) { instance = taskFactory.createNewTaskInstance((ISelection) task); } else if (task instanceof IIteration) { instance = taskFactory.createNewTaskInstance((IIteration) task); } else if (task instanceof IOptional) { instance = taskFactory.createNewTaskInstance((IOptional) task); } else { Event event = createUserInteractionEvent(type, id, matcher.group(4)); instance = taskFactory.createNewTaskInstance((IEventTask) task, event); } // add the children to the instance for (ITaskInstance childInstance : children) { if (instance instanceof ISequenceInstance) { taskBuilder.addChild((ISequenceInstance) instance, childInstance); } else if (instance instanceof ISelectionInstance) { if (((ISelectionInstance) instance).getChild() == null) { taskBuilder.setChild((ISelectionInstance) instance, childInstance); } else { throw new IllegalArgumentException("can not add several children to one " + "selection instance"); } } else if (instance instanceof IIterationInstance) { taskBuilder.addChild((IIterationInstance) instance, childInstance); } else if (instance instanceof IOptionalInstance) { if (((IOptionalInstance) instance).getChild() == null) { taskBuilder.setChild((IOptionalInstance) instance, childInstance); } else { throw new IllegalArgumentException("can not add several children to one " + "optional instance"); } } } return instance; } /** * */ private ITask getCreateTask(String id, String type, List children) { String effectiveId = id; ITask task = tasks.get(effectiveId); if (task == null) { if ("Sequence".equals(type)) { task = taskFactory.createNewSequence(); } else if ("Selection".equals(type)) { task = taskFactory.createNewSelection(); } else if ("Iteration".equals(type)) { task = taskFactory.createNewIteration(); } else if ("Optional".equals(type)) { task = taskFactory.createNewOptional(); } else { effectiveId = type + " --> " + id; task = tasks.get(effectiveId); if (task == null) { task = taskFactory.createNewEventTask(effectiveId); } } tasks.put(effectiveId, task); for (ITaskInstance childInstance : children) { if (task instanceof ISequence) { taskBuilder.addChild((ISequence) task, childInstance.getTask()); } else if (task instanceof ISelection) { taskBuilder.addChild((ISelection) task, childInstance.getTask()); } else if (task instanceof IIteration) { if (((IIteration) task).getMarkedTask() == null) { taskBuilder.setMarkedTask((IIteration) task, childInstance.getTask()); } else if (!((IIteration) task).getMarkedTask().equals(childInstance.getTask())) { throw new IllegalArgumentException ("can not add more than one child to an iteration"); } } else if (task instanceof IOptional) { if (((IOptional) task).getMarkedTask() == null) { taskBuilder.setMarkedTask((IOptional) task, childInstance.getTask()); } else if (!((IOptional) task).getMarkedTask().equals(childInstance.getTask())) { throw new IllegalArgumentException ("can not add more than one child to an optional"); } } else { throw new IllegalArgumentException("can not add children to something that " + "is no sequence, selection, iteration, or " + "optional"); } } } else if ("Selection".equals(type)) { // update the selection with further alternatives, if specified by the current // child instance if (children.size() == 1) { ITask newChildTask = children.get(0).getTask(); boolean found = false; for (ITask childTask : ((ISelection) task).getChildren()) { if (childTask.equals(newChildTask)) { found = true; break; } } if (!found) { taskBuilder.addChild((ISelection) task, newChildTask); } } } else if ("Optional".equals(type)) { // update the optional with what is optional if available if (children.size() == 1) { ITask newChildTask = children.get(0).getTask(); if (((IOptional) task).getMarkedTask() == null) { taskBuilder.setMarkedTask((IOptional) task, newChildTask); } else if (!((IOptional) task).getMarkedTask().equals(newChildTask)) { throw new IllegalArgumentException("all children of the same optional must " + "be of an identical task model."); } } } return task; } /** *

*

* * @param matcher * @return */ private Event createUserInteractionEvent(String type, String targetId, String furtherInfo) { IEventTarget eventTarget = targets.get(targetId); if (eventTarget == null) { eventTarget = new DummyGUIElement(targetId); targets.put(targetId, eventTarget); } IEventType eventType = determineType(type, furtherInfo); return new Event(eventType, eventTarget); } /** *

*

* * @param type * @param enteredText * @return */ private IEventType determineType(String type, String additionalInfo) { if ("TextInput".equals(type)) { return new TextInput(additionalInfo, new ArrayList()); } else if ("MouseClick".equals(type)) { return new MouseClick(MouseButtonInteraction.Button.LEFT, 5, 6); } else if ("Scroll".equals(type)) { int x = 0; int y = 0; if (additionalInfo != null) { String[] parts = additionalInfo.split(" "); if (parts.length > 0) { try { x = Integer.parseInt(parts[0]); } catch (NumberFormatException e) { throw new IllegalArgumentException("could not parse scroll coordinates", e); } } if (parts.length > 1) { try { y = Integer.parseInt(parts[1]); } catch (NumberFormatException e) { throw new IllegalArgumentException("could not parse scroll coordinates", e); } } } return new Scroll(x, y); } else { return new StringEventType(type); } } /** * */ private void correctTargets(ITaskInstanceList taskInstanceList) { Set textInputTargets = new HashSet(); for (ITaskInstance instance : taskInstanceList) { getTextInputTargets(instance, textInputTargets); } Map replacements = new HashMap(); for (IEventTarget oldTarget : textInputTargets) { replacements.put(oldTarget, new DummyTextField(oldTarget.toString())); } for (int i = 0; i < taskInstanceList.size(); i++) { taskBuilder.setTaskInstance (taskInstanceList, i, replaceTargets(taskInstanceList.get(i), replacements)); } } /** * */ private void getTextInputTargets(ITaskInstance instance, Set textInputTargets) { if (instance instanceof ISequenceInstance) { for (ITaskInstance child : (ISequenceInstance) instance) { getTextInputTargets(child, textInputTargets); } } else if (instance instanceof ISelectionInstance) { getTextInputTargets(((ISelectionInstance) instance).getChild(), textInputTargets); } else if (instance instanceof IIterationInstance) { for (ITaskInstance child : (IIterationInstance) instance) { getTextInputTargets(child, textInputTargets); } } else if (instance instanceof IOptionalInstance) { getTextInputTargets(((IOptionalInstance) instance).getChild(), textInputTargets); } else if ((instance instanceof IEventTaskInstance) && (((IEventTaskInstance) instance).getEvent().getType() instanceof TextInput)) { textInputTargets.add(((IEventTaskInstance) instance).getEvent().getTarget()); } } /** * */ private ITaskInstance replaceTargets(ITaskInstance instance, Map replacements) { ITaskInstance replacement = instance; if (instance instanceof ITaskInstanceList) { for (int i = 0; i < ((ITaskInstanceList) instance).size(); i++) { taskBuilder.setTaskInstance ((ITaskInstanceList) instance, i, replaceTargets(((ITaskInstanceList) instance).get(i), replacements)); } } else if (instance instanceof ISelectionInstance) { ISelectionInstance selInst = (ISelectionInstance) instance; taskBuilder.setChild(selInst, replaceTargets(selInst.getChild(), replacements)); } else if (instance instanceof IOptionalInstance) { IOptionalInstance optInst = (IOptionalInstance) instance; if (optInst.getChild() != null) { taskBuilder.setChild(optInst, replaceTargets(optInst.getChild(), replacements)); } } else if ((instance instanceof IEventTaskInstance) && (replacements.containsKey(((IEventTaskInstance) instance).getEvent().getTarget()))) { Event origEvent = ((IEventTaskInstance) instance).getEvent(); replacement = taskFactory.createNewTaskInstance (((IEventTaskInstance) instance).getEventTask(), new Event(origEvent.getType(), replacements.get(origEvent.getTarget()))); taskBuilder.discardTaskInstance(instance); } return replacement; } }