View Javadoc

1   package cz.cuni.amis.pogamut.sposh.elements;
2   
3   import cz.cuni.amis.pogamut.sposh.exceptions.*;
4   import java.awt.datatransfer.DataFlavor;
5   import java.util.Collections;
6   import java.util.LinkedList;
7   import java.util.List;
8   import java.util.logging.Level;
9   import java.util.logging.Logger;
10  
11  /**
12   * Competence is basically a named structure offering several {@link CompetenceElement choices}.
13   * If you imagine a decision tree, the point, where node splits into several
14   * subnodes is akin to the to the {@link Competence}.
15   * <p/>
16   * What is difference between {@link Competence} and {@link DriveCollection}?
17   * Well, for one, DC is in the root of the plan, DC has a goal (well, Cs had
18   * goal too in the past), the main difference how they are processed by the
19   * engine (see description of how {@link DriveCollection} is evaluated).
20   * <p/>
21   * Unlike DC, Competence is simple decision tree, engine will take the element
22   * with highest priority and satisfied trigger and traverses into that node
23   * without looking back.
24   *
25   * @see DriveCollection
26   * @author HonzaH
27   */
28  public final class Competence extends PoshDummyElement<Competence, PoshPlan> implements IParametrizedElement {
29  
30      /**
31       * Name of this competence, can be referenced
32       */
33      private String name;
34      /**
35       * Formal parameters of the competence
36       */
37      private FormalParameters params = new FormalParameters();
38      /**
39       * List of all possible elements (choices) of this competence
40       */
41      private final List<CompetenceElement> elements = new LinkedList<CompetenceElement>();
42      /**
43       * Unmodifiable proxy list of elements
44       */
45      private final List<CompetenceElement> elementsUm = Collections.unmodifiableList(elements);
46      /**
47       * Property string of competence name
48       */
49      public static final String cnName = "cnName";
50      /**
51       * Property string of competence parameters
52       */
53      public static final String cnParams = "cnParams";
54      /**
55       * Data flavor of competence classs, used for drag-and-drop
56       */
57      public static final DataFlavor dataFlavor = new DataFlavor(Competence.class, "competence-node");
58  
59      /**
60       * Create a new Competence with passed name and assign passed elements to
61       * this competence (set parent).
62       *
63       * @param name Name of competence node, it can be referenced
64       * @param elements List of elements that are not part of any other
65       * competence. Shallow copy
66       * @throws FubarException if names of elements are not unique
67       */
68      Competence(String name, FormalParameters params, List<CompetenceElement> elements) throws DuplicateNameException {
69          this(name, params);
70  
71          for (CompetenceElement element : elements) {
72              assert element.getParent() == null;
73              addElement(element);
74          }
75      }
76  
77      /**
78       * Create new competence without {@link CompetenceElement elements}.
79       *
80       * @param name Name of new C
81       * @param params formal parameters of the C
82       */
83      Competence(String name, FormalParameters params) {
84          assert name != null;
85          assert params != null;
86  
87          this.name = name;
88          this.params = new FormalParameters(params);
89      }
90  
91      /**
92       * Add passed element as the last element of this competence and emit.
93       *
94       * @param choice element that will be added into this competence
95       */
96      public void addElement(CompetenceElement choice) throws DuplicateNameException {
97          int beyondLastElementIndex = elementsUm.size();
98          addElement(beyondLastElementIndex, choice);
99      }
100 
101     /**
102      * Add choice as the @index element of all competences choices + emit.
103      *
104      * @param index Index at which should the choice be added
105      * @param choice Choice to add. Orphan.
106      * @throws DuplicateNameException
107      */
108     public void addElement(int index, CompetenceElement choice) throws DuplicateNameException {
109         assert !choice.isChildOfParent();
110 
111         if (isUsedName(choice.getName(), elementsUm)) {
112             throw new DuplicateNameException("Competence " + name + " already has element with name " + choice.getName());
113         }
114 
115         elements.add(index, choice);
116         choice.setParent(this);
117 
118         emitChildNode(choice);
119     }
120 
121     /**
122      * Create text representation of this competence, compatible with parser, so
123      * we can directly output it.
124      *
125      * @return
126      */
127     @Override
128     public String toString() {
129         StringBuilder sb = new StringBuilder();
130 
131         sb.append("\t(C ");
132         sb.append(name);
133         if (!params.isEmpty()) {
134             sb.append(" vars(");
135             sb.append(params.toString());
136             sb.append(')');
137         }
138         sb.append("\n\t\t(elements");
139         for (CompetenceElement element : elements) {
140             // In order to utilize compatibility with older plans, use two braces to enclose the element
141             sb.append("\n\t\t\t(");
142             sb.append(element.toString());
143             sb.append(")");
144         }
145         sb.append("\n\t\t)\n\t)");
146 
147         return sb.toString();
148     }
149 
150     @Override
151     public List<CompetenceElement> getChildDataNodes() {
152         return elementsUm;
153     }
154 
155     /**
156      * @return All choices of this competence.
157      */
158     public List<CompetenceElement> getChoices() {
159         return elementsUm;
160     }
161     
162     /**
163      * Get choice with specified id. Equivalent of {@link #getChoices() }.{@link List#get(int)
164      * }.
165      *
166      * @param choiceId Id of desired choice
167      * @return Found choice.
168      */
169     public CompetenceElement getChoice(int choiceId) {
170         return elementsUm.get(choiceId);
171     }
172 
173     /**
174      * Change name of competence node.
175      *
176      * @param name new name of competence node
177      */
178     public void setName(String name) throws DuplicateNameException, CycleException, InvalidNameException {
179         PoshPlan plan = getRootNode();
180 
181         name = name.trim();
182 
183         if (!name.matches(IDENT_PATTERN)) {
184             throw new InvalidNameException("Name " + name + " is not valid.");
185         }
186 
187         // Check for duplicity
188         if (!this.name.equals(name)) {
189             if (plan != null && !plan.isUniqueNodeName(name)) {
190                 throw new DuplicateNameException("New name for competence '" + this.name + "'(" + name + ") is not unique for reaction plan.");
191             }
192         }
193 
194         String oldName = this.name;
195         this.name = name;
196 
197         if (plan != null && plan.isCycled()) {
198             this.name = oldName;
199             throw new CycleException("New name (" + name + ") for competence '" + this.name + "' is causing cycle.");
200         }
201         firePropertyChange(cnName, oldName, name);
202     }
203 
204     /**
205      * Get name of the competence
206      *
207      * @return name of the competence
208      */
209     @Override
210     public String getName() {
211         return name;
212     }
213 
214     /**
215      * Get HTML description of the competence. Use e.g. in palette of Shed.
216      * @return HTML description of competence
217      */
218     public String getHtmlDescription() {
219         return "<html>Competence: " + getName() + "<br/><pre>" + toString() + "</pre></html>";
220     }
221     
222     @Override
223     public boolean moveChild(int newIndex, PoshElement child) {
224         assert child instanceof CompetenceElement;
225         return moveChildInList(elements, (CompetenceElement) child, newIndex);
226     }
227 
228     @Override
229     public DataFlavor getDataFlavor() {
230         return dataFlavor;
231     }
232 
233     @Override
234     public LapType getType() {
235         return LapType.COMPETENCE;
236     }
237 
238     /**
239      * Remove the {@link CompetenceElement} from this competence and emit
240      * notification about deletion.
241      *
242      * @param element Element to be removed.
243      */
244     public void removeElement(CompetenceElement element) {
245         assert elements.contains(element);
246 
247         if (elements.size() == 1) {
248             String unusedName = getUnusedName("choice-", elementsUm);
249             try {
250                 addElement(LapElementsFactory.createCompetenceElement(unusedName));
251             } catch (DuplicateNameException ex) {
252                 String msg = "Unused name " + unusedName + " is not unused.";
253                 Logger.getLogger(Competence.class.getName()).log(Level.SEVERE, msg, ex);
254                 throw new FubarException(msg, ex);
255             }
256         }
257 
258         int removedElementPosition = elementsUm.indexOf(element);
259 
260         elements.remove(element);
261         element.setParent(null);
262 
263         emitChildDeleted(element, removedElementPosition);
264     }
265 
266     /**
267      * Get list of formal parametrs of competence (names and default values).
268      *
269      * @return
270      */
271     @Override
272     public FormalParameters getParameters() {
273         return params;
274     }
275 
276     @Override
277     public void setParameters(FormalParameters newParams) {
278         FormalParameters oldParams = params;
279         this.params = newParams;
280 
281         firePropertyChange(cnParams, oldParams, newParams);
282     }
283 
284     // TODO: Somehow unify with ActionPattern one.
285     /**
286      * Rename {@link Competence} in the {@link PoshPlan}. This method changes
287      * name fo the comeptence, finds all
288      * {@link TriggeredAction references} to the competence (the ones with old
289      * name) and changed them to @newCompetenceName.
290      *
291      * @param newCompetenceName New name of comeptence after renaming
292      */
293     @Override
294     public void rename(String newCompetenceName) throws InvalidNameException, CycleException, DuplicateNameException {
295         PoshPlan plan = getRootNode();
296         if (plan == null) {
297             throw new IllegalStateException("Competence " + getName() + " is not part of the plan.");
298         }
299 
300         List<TriggeredAction> allReferences = plan.getAllReferences();
301         List<TriggeredAction> referencingActions = new LinkedList<TriggeredAction>();
302 
303         for (TriggeredAction planAction : allReferences) {
304             boolean actionReferencesCompetence = planAction.getName().equals(getName());
305             if (actionReferencesCompetence) {
306                 referencingActions.add(planAction);
307             }
308         }
309         // TODO: rollback if some error happens?
310         for (TriggeredAction referencingAction : referencingActions) {
311             referencingAction.setActionName(newCompetenceName);
312         }
313 
314         this.setName(newCompetenceName);
315     }
316 
317     /**
318      * Get index of @choice.
319      *
320      * @param choice Choice for which we are looking for an index.
321      * @return Found index
322      * @throws IllegalArgumentException If choice is not among choices of this
323      * competence.
324      */
325     public int getChoiceId(CompetenceElement choice) {
326         return getElementId(elementsUm, choice);
327     }
328 }