source: trunk/autoquest-test-utils/src/main/java/de/ugoe/cs/autoquest/tasktrees/TaskTreeDecoder.java @ 1876

Last change on this file since 1876 was 1875, checked in by pharms, 9 years ago
  • added support for mouse clicks
  • fixed bug with undiscarded event task instances
File size: 18.2 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.tasktrees;
16
17import java.util.ArrayList;
18import java.util.HashMap;
19import java.util.HashSet;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.Map;
23import java.util.Set;
24import java.util.regex.Matcher;
25import java.util.regex.Pattern;
26
27import de.ugoe.cs.autoquest.eventcore.Event;
28import de.ugoe.cs.autoquest.eventcore.IEventTarget;
29import de.ugoe.cs.autoquest.eventcore.IEventType;
30import de.ugoe.cs.autoquest.eventcore.StringEventType;
31import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction;
32import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
33import de.ugoe.cs.autoquest.eventcore.gui.Scroll;
34import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
35import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
36import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
37import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
38import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance;
39import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptional;
40import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptionalInstance;
41import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
42import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance;
43import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
44import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance;
45import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
46import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskBuilder;
47import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskFactory;
48import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
49import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstanceList;
50import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
51import de.ugoe.cs.autoquest.test.DummyGUIElement;
52import de.ugoe.cs.autoquest.test.DummyTextField;
53
54/**
55 * TODO comment
56 *
57 * @version $Revision: $ $Date: 01.04.2012$
58 * @author 2012, last modified by $Author: patrick$
59 */
60public class TaskTreeDecoder {
61   
62    /** */
63    private static Pattern taskInstancePattern = Pattern.compile("([^{}]+)\\{|\\}");
64   
65    /** */
66    private static Pattern taskInstanceDetailsPattern =
67        Pattern.compile("\\s*(\\w*)\\s*([\\w\\(\\)\"]*)\\s*(\\(([\\w*\\s*]*)\\))?((\\w*)|(\".*\"))?");
68
69    /** */
70    private ITaskFactory taskFactory;
71   
72    /** */
73    private ITaskBuilder taskBuilder;
74   
75    /** */
76    Map<String, IEventTarget> targets = new HashMap<String, IEventTarget>();
77   
78    /** */
79    Map<String, ITask> tasks = new HashMap<String, ITask>();
80   
81    /**
82     *
83     */
84    public TaskTreeDecoder(ITaskFactory taskFactory, ITaskBuilder taskBuilder) {
85        super();
86        this.taskFactory = taskFactory;
87        this.taskBuilder = taskBuilder;
88    }
89
90    /**
91     *
92     */
93    public ITaskInstanceList decode(String taskTreeSpec) {
94        ITaskInstanceList taskInstanceList = null;
95
96        Matcher taskMatcher = taskInstancePattern.matcher(taskTreeSpec);
97
98        if (taskMatcher.find()) {
99            taskInstanceList = parseTaskInstanceList(taskMatcher);
100        }
101       
102        if (taskMatcher.find()) {
103            throw new IllegalArgumentException("too many tasks specified");
104        }
105       
106        correctTargets(taskInstanceList);
107       
108        // validate the instance list
109        try {
110            new TaskTreeValidator().validate(taskInstanceList);
111        }
112        catch (java.lang.AssertionError e) {
113            throw new IllegalArgumentException(e.getMessage(), e);
114        }
115       
116        return taskInstanceList;
117    }
118
119    /**
120     *
121     */
122    private ITaskInstanceList parseTaskInstanceList(Matcher taskMatcher) {
123        if ("}".equals(taskMatcher.group(1))) {
124            throw new IllegalArgumentException("invalid task instance list specification");
125        }
126       
127        String taskDetails = taskMatcher.group(1);
128       
129        Matcher matcher = taskInstanceDetailsPattern.matcher(taskDetails);
130       
131        if (!matcher.find()) {
132            throw new IllegalArgumentException("could not parse task details");
133        }
134
135        String type = matcher.group(1);
136       
137        ITaskInstanceList list;
138       
139        if ("UserSession".equals(type)) {
140            list = taskFactory.createUserSession();
141        }
142        else if ("TaskInstances".equals(type)) {
143            list = taskFactory.createNewTaskInstance(taskFactory.createNewSequence());
144        }
145        else {
146            throw new IllegalArgumentException("unknown type of task instance list: " + type);
147        }
148       
149        while (taskMatcher.find() && !"}".equals(taskMatcher.group(0))) {
150            ITaskInstance childInstance = parseTaskInstance(taskMatcher);
151           
152            if (!(list instanceof IUserSession)) {
153                taskBuilder.addChild
154                    ((ISequence) ((ITaskInstance) list).getTask(), childInstance.getTask());
155            }
156
157            taskBuilder.addTaskInstance(list, childInstance);
158        }
159
160        return list;
161    }
162
163    /**
164     *
165     */
166    private ITaskInstance parseTaskInstance(Matcher taskMatcher) {
167        if ("}".equals(taskMatcher.group(1))) {
168            throw new IllegalArgumentException("invalid task instance specification");
169        }
170       
171        String taskDetails = taskMatcher.group(1);
172       
173        Matcher matcher = taskInstanceDetailsPattern.matcher(taskDetails);
174       
175        if (!matcher.find()) {
176            throw new IllegalArgumentException("could not parse task details");
177        }
178
179        String type = matcher.group(1);
180        String id = matcher.group(2);
181       
182        // determine the children before creating this instance
183        List<ITaskInstance> children = new LinkedList<ITaskInstance>();
184        while (taskMatcher.find() && !"}".equals(taskMatcher.group(0))) {
185            children.add(parseTaskInstance(taskMatcher));
186        }
187       
188        // get the model
189        ITask task = getCreateTask(id, type, children);
190       
191        // create the respective instance
192        ITaskInstance instance;
193       
194        if (task instanceof ISequence) {
195            instance = taskFactory.createNewTaskInstance((ISequence) task);
196        }
197        else if (task instanceof ISelection) {
198            instance = taskFactory.createNewTaskInstance((ISelection) task);
199        }
200        else if (task instanceof IIteration) {
201            instance = taskFactory.createNewTaskInstance((IIteration) task);
202        }
203        else if (task instanceof IOptional) {
204            instance = taskFactory.createNewTaskInstance((IOptional) task);
205        }
206        else {
207            Event event = createUserInteractionEvent(type, id, matcher.group(4));
208            instance = taskFactory.createNewTaskInstance((IEventTask) task, event);
209        } 
210       
211        // add the children to the instance
212        for (ITaskInstance childInstance : children) {
213            if (instance instanceof ISequenceInstance) {
214                taskBuilder.addChild((ISequenceInstance) instance, childInstance);
215            }
216            else if (instance instanceof ISelectionInstance) {
217                if (((ISelectionInstance) instance).getChild() == null) {
218                    taskBuilder.setChild((ISelectionInstance) instance, childInstance);
219                }
220                else {
221                    throw new IllegalArgumentException("can not add several children to one " +
222                                                       "selection instance");
223                }
224            }
225            else if (instance instanceof IIterationInstance) {
226                taskBuilder.addChild((IIterationInstance) instance, childInstance);
227            }
228            else if (instance instanceof IOptionalInstance) {
229                if (((IOptionalInstance) instance).getChild() == null) {
230                    taskBuilder.setChild((IOptionalInstance) instance, childInstance);
231                }
232                else {
233                    throw new IllegalArgumentException("can not add several children to one " +
234                                                       "optional instance");
235                }
236            }
237        }
238
239        return instance;
240    }
241
242    /**
243     *
244     */
245    private ITask getCreateTask(String id, String type, List<ITaskInstance> children) {
246        String effectiveId = id;
247        ITask task = tasks.get(effectiveId);
248       
249        if (task == null) {
250            if ("Sequence".equals(type)) {
251                task = taskFactory.createNewSequence();
252            }
253            else if ("Selection".equals(type)) {
254                task = taskFactory.createNewSelection();
255            }
256            else if ("Iteration".equals(type)) {
257                task = taskFactory.createNewIteration();
258            }
259            else if ("Optional".equals(type)) {
260                task = taskFactory.createNewOptional();
261            }
262            else {
263                effectiveId = type + " --> " + id;
264                task = tasks.get(effectiveId);
265               
266                if (task == null) {
267                    task = taskFactory.createNewEventTask(effectiveId);
268                }
269            } 
270            tasks.put(effectiveId, task);
271
272            for (ITaskInstance childInstance : children) {
273                if (task instanceof ISequence) {
274                    taskBuilder.addChild((ISequence) task, childInstance.getTask());
275                }
276                else if (task instanceof ISelection) {
277                    taskBuilder.addChild((ISelection) task, childInstance.getTask());
278                }
279                else if (task instanceof IIteration) {
280                    if (((IIteration) task).getMarkedTask() == null) {
281                        taskBuilder.setMarkedTask((IIteration) task, childInstance.getTask());
282                    }
283                    else if (!((IIteration) task).getMarkedTask().equals(childInstance.getTask())) {
284                        throw new IllegalArgumentException
285                            ("can not add more than one child to an iteration");
286                    }
287                }
288                else if (task instanceof IOptional) {
289                    if (((IOptional) task).getMarkedTask() == null) {
290                        taskBuilder.setMarkedTask((IOptional) task, childInstance.getTask());
291                    }
292                    else if (!((IOptional) task).getMarkedTask().equals(childInstance.getTask())) {
293                        throw new IllegalArgumentException
294                            ("can not add more than one child to an optional");
295                    }
296                }
297                else {
298                    throw new IllegalArgumentException("can not add children to something that " +
299                                                       "is no sequence, selection, iteration, or " +
300                                                       "optional");
301                }
302            }
303        }
304        else if ("Selection".equals(type)) {
305            // update the selection with further alternatives, if specified by the current
306            // child instance
307            if (children.size() == 1) {
308                ITask newChildTask = children.get(0).getTask();
309               
310                boolean found = false;
311                for (ITask childTask : ((ISelection) task).getChildren()) {
312                    if (childTask.equals(newChildTask)) {
313                        found = true;
314                        break;
315                    }
316                }
317               
318                if (!found) {
319                    taskBuilder.addChild((ISelection) task, newChildTask);
320                }
321            }
322        }
323        else if ("Optional".equals(type)) {
324            // update the optional with what is optional if available
325            if (children.size() == 1) {
326                ITask newChildTask = children.get(0).getTask();
327               
328                if (((IOptional) task).getMarkedTask() == null) {
329                    taskBuilder.setMarkedTask((IOptional) task, newChildTask);
330                }
331                else if (!((IOptional) task).getMarkedTask().equals(newChildTask)) {
332                    throw new IllegalArgumentException("all children of the same optional must " +
333                                                       "be of an identical task model.");
334                }
335            }
336        }
337
338        return task;
339    }
340
341    /**
342     * <p>
343     * </p>
344     *
345     * @param matcher
346     * @return
347     */
348    private Event createUserInteractionEvent(String type, String targetId, String furtherInfo) {
349        IEventTarget eventTarget = targets.get(targetId);
350        if (eventTarget == null) {
351            eventTarget = new DummyGUIElement(targetId);
352            targets.put(targetId, eventTarget);
353        }
354       
355        IEventType eventType = determineType(type, furtherInfo);
356       
357        return new Event(eventType, eventTarget);
358    }
359
360    /**
361     * <p>
362     * </p>
363     *
364     * @param type
365     * @param enteredText
366     * @return
367     */
368    private IEventType determineType(String type, String additionalInfo) {
369        if ("TextInput".equals(type)) {
370            return new TextInput(additionalInfo, new ArrayList<Event>());
371        }
372        else if ("MouseClick".equals(type)) {
373            return new MouseClick(MouseButtonInteraction.Button.LEFT, 5, 6);
374        }
375        else if ("Scroll".equals(type)) {
376            int x = 0;
377            int y = 0;
378            if (additionalInfo != null) {
379                String[] parts = additionalInfo.split(" ");
380                if (parts.length > 0) {
381                    try {
382                        x = Integer.parseInt(parts[0]);
383                    }
384                    catch (NumberFormatException e) {
385                        throw new IllegalArgumentException("could not parse scroll coordinates", e);
386                    }
387                }
388                if (parts.length > 1) {
389                    try {
390                        y = Integer.parseInt(parts[1]);
391                    }
392                    catch (NumberFormatException e) {
393                        throw new IllegalArgumentException("could not parse scroll coordinates", e);
394                    }
395                }
396            }
397            return new Scroll(x, y);
398        }
399        else {
400            return new StringEventType(type);
401        }
402    }
403
404    /**
405     *
406     */
407    private void correctTargets(ITaskInstanceList taskInstanceList) {
408        Set<IEventTarget> textInputTargets = new HashSet<IEventTarget>();
409       
410        for (ITaskInstance instance : taskInstanceList) {
411            getTextInputTargets(instance, textInputTargets);
412        }
413       
414        Map<IEventTarget, IEventTarget> replacements = new HashMap<IEventTarget, IEventTarget>();
415       
416        for (IEventTarget oldTarget : textInputTargets) {
417            replacements.put(oldTarget, new DummyTextField(oldTarget.toString()));
418        }
419       
420        for (int i = 0; i < taskInstanceList.size(); i++) {
421            taskBuilder.setTaskInstance
422                (taskInstanceList, i, replaceTargets(taskInstanceList.get(i), replacements));
423        }
424       
425    }
426
427    /**
428     *
429     */
430    private void getTextInputTargets(ITaskInstance instance, Set<IEventTarget> textInputTargets) {
431        if (instance instanceof ISequenceInstance) {
432            for (ITaskInstance child : (ISequenceInstance) instance) {
433                getTextInputTargets(child, textInputTargets);
434            }
435        }
436        else if (instance instanceof ISelectionInstance) {
437            getTextInputTargets(((ISelectionInstance) instance).getChild(), textInputTargets);
438        }
439        else if (instance instanceof IIterationInstance) {
440            for (ITaskInstance child : (IIterationInstance) instance) {
441                getTextInputTargets(child, textInputTargets);
442            }
443        }
444        else if (instance instanceof IOptionalInstance) {
445            getTextInputTargets(((IOptionalInstance) instance).getChild(), textInputTargets);
446        }
447        else if ((instance instanceof IEventTaskInstance) &&
448                 (((IEventTaskInstance) instance).getEvent().getType() instanceof TextInput))
449        {
450            textInputTargets.add(((IEventTaskInstance) instance).getEvent().getTarget());
451        }
452    }
453
454    /**
455     *
456     */
457    private ITaskInstance replaceTargets(ITaskInstance                   instance,
458                                         Map<IEventTarget, IEventTarget> replacements)
459    {
460        ITaskInstance replacement = instance;
461        if (instance instanceof ITaskInstanceList) {
462            for (int i = 0; i < ((ITaskInstanceList) instance).size(); i++) {
463                taskBuilder.setTaskInstance
464                    ((ITaskInstanceList) instance, i,
465                     replaceTargets(((ITaskInstanceList) instance).get(i), replacements));
466            }
467        }
468        else if (instance instanceof ISelectionInstance) {
469            ISelectionInstance selInst = (ISelectionInstance) instance;
470            taskBuilder.setChild(selInst, replaceTargets(selInst.getChild(), replacements));
471        }
472        else if (instance instanceof IOptionalInstance) {
473            IOptionalInstance optInst = (IOptionalInstance) instance;
474            if (optInst.getChild() != null) {
475                taskBuilder.setChild(optInst, replaceTargets(optInst.getChild(), replacements));
476            }
477        }
478        else if ((instance instanceof IEventTaskInstance) &&
479                 (replacements.containsKey(((IEventTaskInstance) instance).getEvent().getTarget())))
480        {
481            Event origEvent = ((IEventTaskInstance) instance).getEvent();
482            replacement = taskFactory.createNewTaskInstance
483                (((IEventTaskInstance) instance).getEventTask(),
484                 new Event(origEvent.getType(), replacements.get(origEvent.getTarget())));
485           
486            taskBuilder.discardTaskInstance(instance);
487        }
488       
489        return replacement;
490    }
491
492}
Note: See TracBrowser for help on using the repository browser.