View Javadoc

1   package cz.cuni.amis.pogamut.sposh.elements;
2   
3   import java.util.AbstractList;
4   import java.util.Collections;
5   import java.util.LinkedList;
6   import java.util.List;
7   
8   /**
9    * Trigger of an element. Trigger is basically a collection of senses, when all
10   * senses are evaluated to be true, the trigger is true, otherwise trigger is
11   * false. Note, that trigger can be empty, i.e. contain no senses.
12   * <p/>
13   * Things are little more interesting how should trigger behave when it doesn't
14   * contain any senses. That depends if trigger is used as a goal or trigger.
15   * When goal is true, it means that the element is finished and is no longer
16   * necessary to execute it, thus it is false. Trigger is used to determine if
17   * some element should be traversed and thus it is true. As you can see, default
18   * behavior of trigger is specified in a way that encourages tree traversal
19   * (because default goal result is by false, traversal won't stop prematurely
20   * and because default trigger result is true, traversal will use the node
21   * triggered by the trigger).
22   *
23   * @author HonzaH
24   * @param OWNER type of lap element that owns this trigger
25   */
26  public final class Trigger<OWNER extends PoshElement> extends AbstractList<Sense> {
27  
28      /**
29       * List of senses of this trigger.
30       */
31      private final List<Sense> senses = new LinkedList<Sense>();
32      /**
33       * Unmodifiable list of senses, wrapper of {@link Trigger#senses}.
34       */
35      private final List<Sense> sensesUm = Collections.unmodifiableList(senses);
36      /**
37       * Owner of this trigger.
38       */
39      private final OWNER owner;
40  
41      /**
42       * Create new trigger.
43       *
44       * @param owner owner of this trigger = parent node of all senses in the
45       * trigger.
46       * @param senses list of newly created senses (no parent) for this trigger.
47       */
48      Trigger(OWNER owner, List<Sense> senses) {
49          this.owner = owner;
50  
51          for (Sense sense : senses) {
52              assert sense.getParent() == null;
53              add(sense);
54          }
55      }
56  
57      /**
58       * Create empty trigger.
59       *
60       * @param owner owner of this trigger = parent node of all senses in the
61       * trigger.
62       */
63      Trigger(OWNER owner) {
64          this(owner, Collections.<Sense>emptyList());
65      }
66  
67      /**
68       * Serialize the trigger as serialization of its senses surrounded by
69       * braces. Example: (sense1 (sense2) (sense3))
70       *
71       * @return
72       */
73      @Override
74      public String toString() {
75          StringBuilder sb = new StringBuilder();
76          sb.append('(');
77          boolean first = true;
78  
79          for (Sense sense : sensesUm) {
80              if (first) {
81                  first = false;
82                  sb.append(sense.toString());
83              } else {
84                  sb.append(' ');
85                  sb.append(sense.toString());
86              }
87          }
88  
89          sb.append(')');
90          return sb.toString();
91      }
92  
93      /**
94       * Add sense to this trigger and emit news about addition.
95       *
96       * @param sense sense to be added.
97       */
98      @Override
99      public boolean add(Sense sense) {
100         assert !contains(sense);
101         assert !sense.isChildOfParent();
102 
103         sense.setParent(owner);
104         boolean ret = senses.add(sense);
105 
106         owner.emitChildNode(sense);
107         return ret;
108     }
109 
110     /**
111      * Get sense stored at indexed position in the trigger.
112      *
113      * @param index index of searched trigger.
114      * @return found sense
115      */
116     @Override
117     public Sense get(int index) {
118         return senses.get(index);
119     }
120 
121     /**
122      * Insert sense at specified index and owner will emit new child.
123      *
124      * @param index index where to insert new sense
125      * @param newSense sense to insert into the trigger
126      */
127     @Override
128     public void add(int index, Sense newSense) {
129         assert !contains(newSense);
130         assert !newSense.isChildOfParent();
131 
132         newSense.setParent(owner);
133         senses.add(index, newSense);
134 
135         owner.emitChildNode(newSense);
136     }
137 
138     /**
139      * Remove sense from specified position and the removed sense will emit node
140      * deleted.
141      *
142      * @param index position which sense in the trigger to remove.
143      * @return Removed sense
144      */
145     @Override
146     public Sense remove(int index) {
147         Sense sense = senses.remove(index);
148         sense.setParent(null);
149         owner.emitChildDeleted(sense, index);
150         return sense;
151     }
152 
153     /**
154      * How many senses are there in the trigger
155      *
156      * @return Number of senses in the trigger
157      */
158     @Override
159     public int size() {
160         return senses.size();
161     }
162 
163     /**
164      * Get unmodifiable wrapper of this trigger.
165      *
166      * @return unmodifiable collection that is proxy for this trigger
167      */
168     List<Sense> unmodifiable() {
169         return sensesUm;
170     }
171 
172     /**
173      * Move sense so that after move, it is at newIndex + emit event. If
174      * original index and new index are same, don't emit event.
175      *
176      * @param newIndex New index of the sense. Once move is done, sense will be
177      * at this index and the rest will somehow manage.
178      * @param movedSense Sense that is being moved from its original position to
179      * the @index. The sense must be part of this trigger.
180      */
181     public void moveSense(int newIndex, Sense movedSense) {
182         assert sensesUm.contains(movedSense);
183         assert movedSense.getTrigger() == this;
184 
185         int originalIndex = sensesUm.indexOf(movedSense);
186 
187         if (newIndex > originalIndex) {
188             Sense removed = senses.remove(originalIndex);
189             senses.add(newIndex, movedSense);
190         } else if (newIndex < originalIndex) {
191             senses.remove(originalIndex);
192             senses.add(newIndex, movedSense);
193         } else {
194             return;
195         }
196         owner.emitChildMove(movedSense, originalIndex, newIndex);
197     }
198 }