View Javadoc

1   package cz.cuni.amis.pogamut.sposh.elements;
2   
3   import cz.cuni.amis.pogamut.sposh.exceptions.CycleException;
4   import cz.cuni.amis.pogamut.sposh.exceptions.DuplicateNameException;
5   import java.util.ArrayList;
6   import java.util.List;
7   import java.awt.datatransfer.DataFlavor;
8   import java.io.InputStream;
9   import java.util.Collections;
10  import java.util.HashSet;
11  import java.util.LinkedList;
12  import java.util.Set;
13  
14  /**
15   * This class is root of representation of POSH source file. Basically it
16   * represents the outest brackets.
17   *
18   * This class holds Competences, APs and DriveElement.
19   *
20   * @author Honza
21   */
22  public final class PoshPlan extends PoshDummyElement {
23  
24      private DocString _docstring = null;
25      private DriveCollection _driveCollection = null;
26      private List<ActionPattern> _actionPatterns = new ArrayList<ActionPattern>();
27      private List<Competence> _competences = new ArrayList<Competence>();
28  
29      /**
30       * Get names of all actions in this plan.
31       *
32       * @return set of action names in this plan.
33       */
34      public Set<String> getActionsNames() {
35          Set<String> result = new HashSet<String>();
36          // Add actions from APs
37          for (ActionPattern ap : this._actionPatterns) {
38              for (TriggeredAction action : ap.getTriggeredActions()) {
39                  String actionName = action.getName();
40                  if (!isAP(actionName) && !isC(actionName)) {
41                      result.add(actionName);
42                  }
43              }
44          }
45          // Actions in Cs
46          for (Competence c : this._competences) {
47              for (CompetenceElement ce : c.getChildDataNodes()) {
48                  String actionName = ce.getAction().getName();
49                  if (!isAP(actionName) && !isC(actionName)) {
50                      result.add(actionName);
51                  }
52              }
53          }
54          // Actions in drives
55          for (DriveElement de : _driveCollection.getDrives()) {
56              String actionName = de.getTriggeredAction().getName();
57              if (!isAP(actionName) && !isC(actionName)) {
58                  result.add(actionName);
59              }
60          }
61          return result;
62      }
63  
64      /**
65       * Get names of all senses in this plan.
66       *
67       * @return set of sense names in this plan.
68       */
69      public Set<String> getSensesNames() {
70          Set<String> result = new HashSet<String>();
71          // Goal of DC
72          Goal dcGoal = this._driveCollection.getGoal();
73          if (dcGoal != null) {
74              for (Sense sense : dcGoal.getSenses()) {
75                  result.add(sense.getSenseName());
76              }
77          }
78          // Get sense names of drives
79          for (DriveElement de : _driveCollection.getDrives()) {
80              for (Sense sense : de.getTriggers().getSenses()) {
81                  result.add(sense.getSenseName());
82              }
83          }
84          // Get senses in C
85          for (Competence c : this._competences) {
86              for (CompetenceElement ce : c.getChildDataNodes()) {
87                  for (Sense sense : ce.getTriggerSenses()) {
88                      result.add(sense.getSenseName());
89                  }
90              }
91          }
92          return result;
93      }
94  
95      /**
96       * Does this plan contain C with specified name?
97       *
98       * @param name name of C we are checking
99       * @return true if C with name is in this plane, false if not.
100      */
101     public boolean isC(String name) {
102         return getC(name) != null;
103     }
104 
105     /**
106      * Return competence from this plan with specified name.
107      *
108      * @param name name of searched C
109      * @return competence or null if it doesn't exists
110      */
111     public Competence getC(String name) {
112         for (Competence c : getCompetences()) {
113             if (c.getName().equals(name)) {
114                 return c;
115             }
116         }
117         return null;
118     }
119 
120     /**
121      * Does this plan contain AP with specified name?
122      *
123      * @param name name of AP we are checking
124      * @return true if AP with name is in this plane, false if not.
125      */
126     public boolean isAP(String name) {
127         return getAP(name) != null;
128     }
129 
130     /**
131      * Return action pattern from this plan with specified name.
132      *
133      * @param name name of searched AP
134      * @return action pattern or null if it doesn't exists
135      */
136     public ActionPattern getAP(String name) {
137         for (ActionPattern ap : getActionPatterns()) {
138             if (ap.getName().equals(name)) {
139                 return ap;
140             }
141         }
142         return null;
143     }
144 
145     /**
146      * Check if passed string is different than names of all competences and
147      * action patterns. If it is, it can be used as name of new competence or
148      * action pattern.
149      *
150      * @param id name of tested string.
151      * @return true if it is unique, false otherwise.
152      */
153     public boolean isUniqueAPorComp(String id) {
154         for (ActionPattern node : _actionPatterns) {
155             if (node.getName().equals(id)) {
156                 return false;
157             }
158         }
159 
160         for (Competence node : _competences) {
161             if (node.getName().equals(id)) {
162                 return false;
163             }
164         }
165 
166         return true;
167     }
168 
169     /**
170      * Go throught the tree and for every trigger action refresh the name of the
171      * action (change from X to X).
172      *
173      * Do this when new competence arrives because some action can have same
174      * name as the newly made competence.
175      *
176      * Actions are refreshed from leafs to root order.
177      *
178      * THIS DOESN'T CHECK IF THERE IS A CYCLE, IT MUST BE DONE IN UPPER
179      * FUNCTION.
180      */
181     private void refreshActions() {
182         //refreshActions(this);
183         // First gather all actions
184         List<TriggeredAction> actions = new LinkedList<TriggeredAction>();
185 
186         List<ActionPattern> aps = this.getActionPatterns();
187         List<Competence> competences = this.getCompetences();
188 
189         for (ActionPattern ap : aps) {
190             for (TriggeredAction action : ap.getTriggeredActions()) {
191                 actions.add(action);
192             }
193         }
194         for (Competence competence : competences) {
195             for (CompetenceElement ce : competence.getChildDataNodes()) {
196                 actions.add(ce.getAction());
197             }
198         }
199 
200         if (getDriveCollection() != null) {
201             for (DriveElement de : getDriveCollection().getDrives()) {
202                 actions.add(de.getTriggeredAction());
203             }
204         }
205         // And then refresh them
206         for (TriggeredAction action : actions) {
207             action.setActionName(action.getName());
208         }
209     }
210 
211     /**
212      * Yay, this function adds documentation node to the tree and emits the new
213      * child.
214      *
215      * @param documentationNode
216      */
217     public void setDocString(DocString docstring) {
218         docstring.setParent(this);
219         if (this._docstring != null) {
220             this._docstring.remove();
221         }
222         this._docstring = docstring;
223         emitChildNode(_docstring);
224     }
225 
226     /**
227      * Add competence node to the POSH tree (add, emit)
228      *
229      * @param competenceNode
230      */
231     public void addCompetence(Competence competence) throws DuplicateNameException, CycleException {
232         if (!this.isUniqueAPorComp(competence.getName())) {
233             throw new DuplicateNameException("Competence  '" + competence.getName() + "' has duplicate name in POSH plan.");
234         }
235 
236         competence.setParent(this);
237         _competences.add(competence);
238 
239         if (this.isCycled()) {
240             _competences.remove(competence);
241             throw new CycleException("Competence " + competence.getName() + " is causing cycle.");
242         }
243 
244         emitChildNode(competence);
245 
246         refreshActions();
247     }
248 
249     /**
250      * Get unmodifiable array of all competences.
251      *
252      * @return
253      */
254     public List<Competence> getCompetences() {
255         return Collections.unmodifiableList(this._competences);
256     }
257 
258     /**
259      * Add new action pattern to the POSH tree root (add, set parents, emit)
260      *
261      * @param actionPatternNode
262      */
263     public void addActionPattern(ActionPattern actionPattern) throws DuplicateNameException, CycleException {
264         if (!this.isUniqueAPorComp(actionPattern.getName())) {
265             throw new DuplicateNameException("Action pattern '" + actionPattern.getName() + "' has duplicate name in POSH plan.");
266         }
267 
268         actionPattern.setParent(this);
269         _actionPatterns.add(actionPattern);
270 
271         // test cycle
272         if (this.isCycled()) {
273             _actionPatterns.remove(actionPattern);
274             throw new CycleException("Action pattern '" + actionPattern.getName() + "' is causing cycle.");
275         }
276 
277         emitChildNode(actionPattern);
278 
279         refreshActions();
280     }
281 
282     /**
283      * Get unmodifiable array of all competences.
284      *
285      * @return
286      */
287     public List<ActionPattern> getActionPatterns() {
288         return Collections.unmodifiableList(this._actionPatterns);
289     }
290 
291     /**
292      * Set the drive collection. I don't think this should be ever used.
293      *
294      * @param driveCollection
295      */
296     protected void setDriveCollection(DriveCollection dc) {
297         dc.setParent(this);
298         if (this._driveCollection != null) {
299             this._driveCollection.remove();
300         }
301 
302         this._driveCollection = dc;
303 
304         emitChildNode(this._driveCollection);
305     }
306 
307     public DriveCollection getDriveCollection() {
308         return this._driveCollection;
309     }
310 
311     /**
312      * Is POSH plan cycled?
313      *
314      * @return true if plan has a cycle, false otherwise
315      */
316     public boolean isCycled() {
317 
318         for (ActionPattern apNode : this._actionPatterns) {
319             if (findCycle(apNode, new HashSet<String>())) {
320                 return true;
321             }
322         }
323         for (Competence compNode : this._competences) {
324             if (findCycle(compNode, new HashSet<String>())) {
325                 return true;
326             }
327         }
328         return false;
329     }
330 
331     private boolean findCycle(ActionPattern apNode, Set<String> set) {
332         if (set.contains(apNode.getName())) {
333             return true;
334         }
335         set.add(apNode.getName());
336 
337         for (TriggeredAction action : apNode._actions) {
338             ActionPattern actionAP;
339             if ((actionAP = getAP(action.getName())) != null) {
340                 if (findCycle(actionAP, set)) {
341                     return true;
342                 }
343             }
344 
345             Competence actionComp;
346             if ((actionComp = getC(action.getName())) != null) {
347                 if (findCycle(actionComp, set)) {
348                     return true;
349                 }
350             }
351         }
352         set.remove(apNode.getName());
353         return false;
354     }
355 
356     private boolean findCycle(Competence compNode, Set<String> set) {
357         if (set.contains(compNode.getName())) {
358             return true;
359         }
360 
361         set.add(compNode.getName());
362 
363         for (CompetenceElement cAtom : compNode.getChildDataNodes()) {
364             TriggeredAction action = cAtom.getAction();
365 
366             ActionPattern actionAP;
367             if ((actionAP = getAP(action.getName())) != null) {
368                 if (findCycle(actionAP, set)) {
369                     return true;
370                 }
371             }
372 
373             Competence actionComp;
374             if ((actionComp = getC(action.getName())) != null) {
375                 if (findCycle(actionComp, set)) {
376                     return true;
377                 }
378             }
379         }
380         set.remove(compNode.getName());
381         return false;
382     }
383 
384     /**
385      * Try to find AP or competence with same name as <tt>ident</tt> and if it
386      * exists, return list of all actions the AP or competence has. If no such
387      * Ap or Competence exists, return empty list.
388      *
389      * Used for detection of cycles.
390      *
391      * @return list of names of actions that can be invoked by executing AP or
392      * competence with name <tt>ident</tt>. If nothing found, return empty list.
393      */
394     protected List<String> getAPorCompActions(String ident) {
395         ActionPattern ap = this.getAP(ident);
396         if (ap != null) {
397             LinkedList<String> actionList = new LinkedList<String>();
398 
399             for (TriggeredAction a : ap._actions) {
400                 actionList.add(a.getName());
401             }
402             return actionList;
403         }
404 
405         Competence cn = this.getC(ident);
406         if (cn != null) {
407             LinkedList<String> actionList = new LinkedList<String>();
408             for (CompetenceElement ca : cn.getChildDataNodes()) {
409                 if (ca.getAction() != null) {
410                     actionList.add(ca.getAction().getName());
411                 }
412             }
413             return actionList;
414         }
415         return new LinkedList<String>();
416     }
417 
418     /**
419      * Return lap of posh tree.
420      */
421     @Override
422     public String toString() {
423         String ret = "(";
424 
425         if (this._docstring != null) {
426             ret += "\n" + this._docstring.toString();
427         }
428 
429         for (Competence node : _competences) {
430             ret += "\n" + node.toString();
431         }
432 
433         for (ActionPattern node : _actionPatterns) {
434             ret += "\n" + node.toString();
435         }
436 
437         if (this._driveCollection != null) {
438             ret += "\n" + this._driveCollection.toString();
439         }
440 
441         ret += "\n)";
442         return ret;
443     }
444 
445     /**
446      * Get all nodes that are connected directly to this one (= documentration,
447      * actonPatterns, competences, DriveCollections).
448      *
449      * The list is generated every time.
450      *
451      * @return All children of this node.
452      */
453     @Override
454     public List<PoshElement> getChildDataNodes() {
455         List<PoshElement> children = new ArrayList<PoshElement>();
456 
457         if (this._docstring != null) {
458             children.add(this._docstring);
459         }
460 
461         for (Competence competence : _competences) {
462             children.add(competence);
463         }
464 
465         for (ActionPattern actionPattern : _actionPatterns) {
466             children.add(actionPattern);
467         }
468 
469         if (this._driveCollection != null) {
470             children.add(this._driveCollection);
471         }
472 
473         return children;
474     }
475 
476     /**
477      * Go through all nodes in the tree and remove listener from all elements it
478      * is registered to.
479      *
480      * @param listener listener we want to remove from the tree.
481      */
482     public synchronized void removeListenersFromTree(PoshElementListener listener) {
483         getRootNode().removeListenersFromTree(this, listener);
484     }
485 
486     /**
487      * Remove listener <tt>listener</tt> from the tree with root node.
488      */
489     protected synchronized void removeListenersFromTree(PoshElement node, PoshElementListener listener) {
490         node.removeElementListener(listener);
491 
492         for (PoshElement child : node.getChildDataNodes()) {
493             removeListenersFromTree(child, listener);
494         }
495     }
496 
497     @Override
498     public boolean moveChild(PoshElement child, int relativePosition) {
499         if (this._competences.contains(child)) {
500             return moveNodeInList(_competences, child, relativePosition);
501         }
502         if (this._actionPatterns.contains(child)) {
503             return moveNodeInList(_actionPatterns, child, relativePosition);
504         }
505         return false;
506     }
507     public static final DataFlavor dataFlavor = new DataFlavor(PoshPlan.class, "posh_tree_root");
508 
509     @Override
510     public DataFlavor getDataFlavor() {
511         return dataFlavor;
512     }
513 
514     @Override
515     public void addChildDataNode(PoshElement newChild) throws ParseException {
516         if (newChild instanceof DocString) {
517             this.setDocString((DocString) newChild);
518         } else if (newChild instanceof Competence) {
519             this.addCompetence((Competence) newChild);
520         } else if (newChild instanceof ActionPattern) {
521             this.addActionPattern((ActionPattern) newChild);
522         } else if (newChild instanceof DriveCollection) {
523             this.setDriveCollection((DriveCollection) newChild);
524         } else {
525             throw new RuntimeException("Class " + newChild.getClass().getSimpleName() + " not accepted.");
526         }
527 
528     }
529 
530     @Override
531     public void neutralizeChild(PoshElement childNode) {
532         if (this._docstring == childNode) {
533             this._docstring = null;
534             childNode.remove();
535         } else if (this._competences.contains(childNode)) {
536             this._competences.remove(childNode);
537             childNode.remove();
538         } else if (this._actionPatterns.contains(childNode)) {
539             this._actionPatterns.remove(childNode);
540             childNode.remove();
541         } else if (this._driveCollection == childNode) {
542             this._driveCollection = null;
543             childNode.remove();
544         }
545     }
546 
547     /**
548      * Emit the tree using BFS. <p> For every element emit it as new one.
549      *
550      * @param root
551      */
552     public void emitTree() {
553         for (PoshElement pe : this.getChildDataNodes()) {
554             this.emitChildNode(pe);
555         }
556     }
557 
558     /**
559      * Synchronize the plan to another.
560      *
561      * @param stream
562      */
563     public void synchronizePlan(InputStream stream) throws ParseException, CycleException {
564         PoshParser parser = new PoshParser(stream);
565         PoshPlan parsedPlan = parser.parsePlan();
566 
567         // remove old stuff
568         if (_docstring != null) {
569             neutralizeChild(_docstring);
570         }
571         if (_driveCollection != null) {
572             neutralizeChild(_driveCollection);
573         }
574 
575         ActionPattern[] aps = getActionPatterns().toArray(new ActionPattern[0]);
576         for (ActionPattern ap : aps) {
577             neutralizeChild(ap);
578         }
579 
580         Competence[] cs = getCompetences().toArray(new Competence[0]);
581         for (Competence c : cs) {
582             neutralizeChild(c);
583         }
584 
585         // copy new stuff from new plan
586         this.setDocString(parsedPlan._docstring);
587 
588         this.setDriveCollection(parsedPlan._driveCollection);
589         for (ActionPattern ap : parsedPlan.getActionPatterns()) {
590             addActionPattern(ap);
591         }
592         for (Competence c : parsedPlan.getCompetences()) {
593             addCompetence(c);
594         }
595     }
596 }