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

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