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

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