f94cb2ef40d96819562827dcd740a6f45386ff91
[policy/apex-pdp.git] / model / policy-model / src / main / java / org / onap / policy / apex / model / policymodel / concepts / AxState.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
4  *  Modifications Copyright (C) 2018 Samsung Electronics Co., Ltd.
5  *  Modifications Copyright (C) 2019 Nordix Foundation.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  * SPDX-License-Identifier: Apache-2.0
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.policy.apex.model.policymodel.concepts;
24
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.Set;
29 import java.util.TreeMap;
30 import java.util.TreeSet;
31
32 import javax.persistence.AttributeOverride;
33 import javax.persistence.AttributeOverrides;
34 import javax.persistence.CascadeType;
35 import javax.persistence.CollectionTable;
36 import javax.persistence.Column;
37 import javax.persistence.ElementCollection;
38 import javax.persistence.Embedded;
39 import javax.persistence.EmbeddedId;
40 import javax.persistence.Entity;
41 import javax.persistence.JoinColumn;
42 import javax.persistence.JoinTable;
43 import javax.persistence.ManyToMany;
44 import javax.persistence.OneToOne;
45 import javax.persistence.Table;
46 import javax.xml.bind.Unmarshaller;
47 import javax.xml.bind.annotation.XmlAccessType;
48 import javax.xml.bind.annotation.XmlAccessorType;
49 import javax.xml.bind.annotation.XmlElement;
50 import javax.xml.bind.annotation.XmlRootElement;
51 import javax.xml.bind.annotation.XmlType;
52
53 import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
54 import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
55 import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
56 import org.onap.policy.apex.model.basicmodel.concepts.AxKeyUse;
57 import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
58 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
59 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
60 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
61 import org.onap.policy.common.utils.validation.Assertions;
62
63 /**
64  * This class holds the definition of a single state in a policy. A state is a single stage in a policy. A state has a
65  * single input event, its trigger. A state can output many events, but can only output one event on a single execution.
66  * After it executes, a state can pass control to another state or can simply emit its event to an external system. In
67  * the case where a state passes control to another state, the output event of the state becomes the input event of the
68  * next state. The outputs of a state {@link AxStateOutput} are held as a map in the state. Each state output contains
69  * the outgoing event of the state and optionally the next state to pass control to.
70  *
71  * <p>A state uses tasks {@link AxTask} to execute its logic. A state holds its tasks in a map and must have at least
72  * one task. A state uses Task Selection Logic {@link AxTaskSelectionLogic} to select which task should be executed in a
73  * given execution cycle. Optional Task Selection Logic can use fields on the incoming event and information from the
74  * context albums available on the state to decide what task to execute in a given context. The default task of a state
75  * is the task that is executed when task Selection Logic is not specified. In cases where only a single task is
76  * specified on a state, the default task must be that task and the state always executes that task.
77  *
78  * <p>What happens when a state completes its execution cycle depends on the task that is selected for execution by the
79  * state. Therefore, the action to be performed a state on execution of each task must be defined in the state as a
80  * {@link AxStateTaskReference} instance for each task defined in the state. The {@link AxStateTaskReference} instance
81  * defines the action to be performed as either a {@link AxStateTaskOutputType} of {@link AxStateTaskOutputType#DIRECT}
82  * or {@link AxStateTaskOutputType#LOGIC} and contains an {@link AxReferenceKey} reference to the instance that will
83  * complete the state output.
84  *
85  * <p>In the case of direct output, the {@link AxReferenceKey} reference in the {@link AxStateTaskReference} instance is
86  * a reference to an {@link AxStateOutput} instance. The state output defines the event to be emitted by the state and
87  * the next state to pass control to if any. All fields of the executed task are marshaled onto the outgoing event
88  * automatically by Apex.
89  *
90  * <p>In the case of logic output, the {@link AxReferenceKey} reference in the {@link AxStateTaskReference} instance is
91  * a reference to State Finalizer Logic in an {@link AxStateFinalizerLogic} instance, which selects the
92  * {@link AxStateOutput} that the state will use. The state finalizer logic uses fields emitted by the executed task and
93  * information from the context albums available on the state to decide what {@link AxStateOutput} to select in a given
94  * context. The state output defines the event to be emitted by the state and the next state to pass control to if any.
95  * The State Finalizer Logic instances for the state are held in a map in the state. State Finalizer Logic must marshal
96  * the fields of the output event in whatever manner it wishes; Apex does not automatically transfer the output fields
97  * from the task directly to the output event.
98  *
99  * <p>The Task Selection Logic instance or State Finalizer Logic instances in a state may use information in context
100  * albums to arrive at their task or state output selections. The context albums that the state uses and that should be
101  * made available to the state by Apex policy distribution are held as a set of references to context albums in the
102  * state.
103  *
104  * <p>During validation of a state, the validation checks listed below are executed: <ol> <li>The policy key must not be
105  * a null key and must be valid, see validation in {@link AxReferenceKey} <li>The trigger event key must not be a null
106  * key and must be valid, see validation in {@link AxArtifactKey} <li>At least one state output must be defined <li>Each
107  * state output in a state must have that state as its parent <li>Each state output must be valid, see validation in
108  * {@link AxStateOutput} <li>The next state defined in a state output must be unique in a state <li>The default task key
109  * must not be a null key and must be valid, see validation in {@link AxArtifactKey} <li>The default task must appear in
110  * the task map of the state <li>At least one task must be defined on the state <li>Each task key on the task map for
111  * the state must not be a null key and must be valid, see validation in {@link AxArtifactKey} <li>All state task
112  * references for each task in the state must exist and must be valid, see validation in {@link AxStateTaskReference}
113  * <li>Each state task reference in a state must have that state as its parent <li>For direct state outputs from tasks,
114  * the state output must be defined on the state <li>For logic state outputs from tasks, the State Finalizer Logic must
115  * be defined on the state <li>An observation is issued for each state output defined on the state that is not used as a
116  * direct output on a task <li>An observation is issued for each state finalizer logic instance defined on the state
117  * that is not used as an output on a task <li>Each context album key on the context album set for the state must not be
118  * a null key and must be valid, see validation in {@link AxArtifactKey} <li>Task Selection logic in a state must have
119  * that state as its parent <li>Task Selection logic in a state must be valid, see validation in
120  * {@link AxTaskSelectionLogic} <li>Each State Finalizer logic instance in a state must have that state as its parent
121  * <li>Each State Finalizer logic instance in a state must be valid, see validation in {@link AxStateFinalizerLogic}
122  * </ol>
123  */
124
125 @Entity
126 @Table(name = "AxState")
127
128 @XmlAccessorType(XmlAccessType.FIELD)
129 @XmlRootElement(name = "apexState", namespace = "http://www.onap.org/policy/apex-pdp")
130 @XmlType(name = "AxState", namespace = "http://www.onap.org/policy/apex-pdp", propOrder =
131     { "key", "trigger", "stateOutputs", "contextAlbumReferenceSet", "taskSelectionLogic", "stateFinalizerLogicMap",
132                     "defaultTask", "taskReferenceMap" })
133
134 public class AxState extends AxConcept {
135     private static final String DOES_NOT_EQUAL_STATE_KEY = " does not equal state key";
136
137     private static final long serialVersionUID = 8041771382337655535L;
138
139     @EmbeddedId
140     @XmlElement(name = "stateKey", required = true)
141     private AxReferenceKey key;
142
143     // @formatter:off
144     @Embedded
145     @AttributeOverrides({@AttributeOverride(name = "name", column = @Column(name = "inTriggerName")),
146             @AttributeOverride(name = "version", column = @Column(name = "inTriggerVersion"))})
147     @Column(name = "trigger")
148     @XmlElement(required = true)
149     private AxArtifactKey trigger;
150
151     @ManyToMany(cascade = CascadeType.ALL)
152     @JoinTable(
153             joinColumns = {@JoinColumn(name = "soParentKeyName", referencedColumnName = "parentKeyName"),
154                     @JoinColumn(name = "soParentKeyVersion", referencedColumnName = "parentKeyVersion"),
155                     @JoinColumn(name = "soParentLocalName", referencedColumnName = "parentLocalName"),
156                     @JoinColumn(name = "soLocalName", referencedColumnName = "localName")},
157             inverseJoinColumns = {@JoinColumn(name = "stateParentKeyName", referencedColumnName = "parentKeyName"),
158                     @JoinColumn(name = "stateParentKeyVersion", referencedColumnName = "parentKeyVersion"),
159                     @JoinColumn(name = "stateParentLocalName", referencedColumnName = "parentLocalName"),
160                     @JoinColumn(name = "stateLocalName", referencedColumnName = "localName")})
161     @XmlElement(name = "stateOutputs", required = true)
162     private Map<String, AxStateOutput> stateOutputs;
163
164     @ElementCollection
165     @CollectionTable(joinColumns = {@JoinColumn(name = "stateParentKeyName", referencedColumnName = "parentKeyName"),
166             @JoinColumn(name = "stateParentKeyVersion", referencedColumnName = "parentKeyVersion"),
167             @JoinColumn(name = "stateParentLocalName", referencedColumnName = "parentLocalName"),
168             @JoinColumn(name = "stateLocalName", referencedColumnName = "localName")})
169     @XmlElement(name = "contextAlbumReference")
170     private Set<AxArtifactKey> contextAlbumReferenceSet;
171
172     @OneToOne
173     @JoinTable(name = "STATE_TSL_JT",
174             joinColumns = {
175                     @JoinColumn(name = "tslParentKeyName", referencedColumnName = "parentKeyName", updatable = false,
176                             insertable = false),
177                     @JoinColumn(name = "tslParentKeyVersion", referencedColumnName = "parentKeyVersion",
178                             updatable = false, insertable = false),
179                     @JoinColumn(name = "tslParentLocalName ", referencedColumnName = "parentLocalName",
180                             updatable = false, insertable = false),
181                     @JoinColumn(name = "tslLocalName", referencedColumnName = "localName", updatable = false,
182                             insertable = false)})
183     @XmlElement(required = true)
184     private AxTaskSelectionLogic taskSelectionLogic;
185
186     @ManyToMany(cascade = CascadeType.ALL)
187     @JoinTable(
188             joinColumns = {@JoinColumn(name = "sflParentKeyName", referencedColumnName = "parentKeyName"),
189                     @JoinColumn(name = "sflParentKeyVersion", referencedColumnName = "parentKeyVersion"),
190                     @JoinColumn(name = "sflParentLocalName", referencedColumnName = "parentLocalName"),
191                     @JoinColumn(name = "sflLocalName", referencedColumnName = "localName")},
192             inverseJoinColumns = {@JoinColumn(name = "stateParentKeyName", referencedColumnName = "parentKeyName"),
193                     @JoinColumn(name = "stateParentKeyVersion", referencedColumnName = "parentKeyVersion"),
194                     @JoinColumn(name = "stateParentLocalName", referencedColumnName = "parentLocalName"),
195                     @JoinColumn(name = "stateLocalName", referencedColumnName = "localName")})
196     @XmlElement(name = "stateFinalizerLogicMap", required = true)
197     private Map<String, AxStateFinalizerLogic> stateFinalizerLogicMap;
198
199     @Embedded
200     @AttributeOverrides({@AttributeOverride(name = "name", column = @Column(name = "defaultTaskName")),
201             @AttributeOverride(name = "version", column = @Column(name = "defaultTaskVersion"))})
202     @Column(name = "defaultTask")
203     @XmlElement(required = true)
204     private AxArtifactKey defaultTask;
205
206     @ManyToMany(cascade = CascadeType.ALL)
207     @JoinTable(
208             joinColumns = {@JoinColumn(name = "trmParentKeyName", referencedColumnName = "parentKeyName"),
209                     @JoinColumn(name = "trmParentKeyVersion", referencedColumnName = "parentKeyVersion"),
210                     @JoinColumn(name = "trmParentLocalName", referencedColumnName = "parentLocalName"),
211                     @JoinColumn(name = "trmLocalName", referencedColumnName = "localName")},
212             inverseJoinColumns = {@JoinColumn(name = "stateParentKeyName", referencedColumnName = "parentKeyName"),
213                     @JoinColumn(name = "stateParentKeyVersion", referencedColumnName = "parentKeyVersion"),
214                     @JoinColumn(name = "stateParentLocalName", referencedColumnName = "parentLocalName"),
215                     @JoinColumn(name = "stateLocalName", referencedColumnName = "localName")})
216     @XmlElement(name = "taskReferences", required = true)
217     private Map<AxArtifactKey, AxStateTaskReference> taskReferenceMap;
218     // @formatter:on
219
220     /**
221      * The Default Constructor creates a state with a null reference key and with default values for all other fields.
222      */
223     public AxState() {
224         this(new AxReferenceKey());
225         contextAlbumReferenceSet = new TreeSet<>();
226         taskReferenceMap = new TreeMap<>();
227     }
228
229     /**
230      * Copy constructor.
231      *
232      * @param copyConcept the concept to copy from
233      */
234     public AxState(final AxState copyConcept) {
235         super(copyConcept);
236     }
237
238     /**
239      * The Keyed Constructor creates a state with the given reference key and with default values for all other fields.
240      *
241      * @param key the reference key of the state
242      */
243     public AxState(final AxReferenceKey key) {
244         // @formatter:off
245         this(new AxStateParamsBuilder()
246                         .key(key)                                                             // Key
247                         .trigger(AxArtifactKey.getNullKey())                                  // Trigger Reference
248                         .stateOutputs(new TreeMap<String, AxStateOutput>())                   // State Outputs
249                         .contextAlbumReferenceSet(new TreeSet<AxArtifactKey>())               // Context Album Refs
250                         .taskSelectionLogic(new AxTaskSelectionLogic())                       // Task Selection Logic
251                         .stateFinalizerLogicMap(new TreeMap<String, AxStateFinalizerLogic>()) // State Finalizer Logics
252                         .defaultTask(AxArtifactKey.getNullKey())                              // Default Task
253                         .taskReferenceMap(new TreeMap<AxArtifactKey, AxStateTaskReference>()) // Task References
254         );
255         // @formatter:on
256     }
257
258     /**
259      * This Constructor creates a state with all its fields defined.
260      *
261      * @param axStateParams parameters for state creation
262      */
263     // CHECKSTYLE:OFF: checkstyle:parameterNumber
264     public AxState(AxStateParamsBuilder axStateParams) {
265         super();
266         Assertions.argumentNotNull(axStateParams.getKey(), "key may not be null");
267         Assertions.argumentNotNull(axStateParams.getTrigger(), "trigger may not be null");
268         Assertions.argumentNotNull(axStateParams.getStateOutputs(), "stateOutputs may not be null");
269         Assertions.argumentNotNull(axStateParams.getContextAlbumReferenceSet(),
270                         "contextAlbumReferenceSet may not be null");
271         Assertions.argumentNotNull(axStateParams.getTaskSelectionLogic(), "taskSelectionLogic may not be null");
272         Assertions.argumentNotNull(axStateParams.getStateFinalizerLogicMap(), "stateFinalizerLogicMap may not be null");
273         Assertions.argumentNotNull(axStateParams.getDefaultTask(), "defaultTask may not be null");
274         Assertions.argumentNotNull(axStateParams.getTaskReferenceMap(), "taskReferenceMap may not be null");
275
276         this.key = axStateParams.getKey();
277         this.trigger = axStateParams.getTrigger();
278         this.stateOutputs = axStateParams.getStateOutputs();
279         this.contextAlbumReferenceSet = axStateParams.getContextAlbumReferenceSet();
280         this.taskSelectionLogic = axStateParams.getTaskSelectionLogic();
281         this.stateFinalizerLogicMap = axStateParams.getStateFinalizerLogicMap();
282         this.defaultTask = axStateParams.getDefaultTask();
283         this.taskReferenceMap = axStateParams.getTaskReferenceMap();
284     }
285     // CHECKSTYLE:ON: checkstyle:parameterNumber
286
287     /**
288      * When a state is unmarshalled from disk or from the database, the parent of contained objects is not defined. This
289      * method is called by JAXB after unmarshaling and is used to set the parent keys of all
290      * {@link AxTaskSelectionLogic}, {@link AxStateOutput}, and {@link AxStateFinalizerLogic} instance in the state.
291      *
292      * @param unmarshaler the unmarshaler that is unmarshaling the model
293      * @param parent the parent object of this object in the unmarshaler
294      */
295     public void afterUnmarshal(final Unmarshaller unmarshaler, final Object parent) {
296         if (!taskSelectionLogic.getKey().getLocalName().equals(AxKey.NULL_KEY_NAME)) {
297             taskSelectionLogic.getKey().setParentReferenceKey(key);
298         }
299
300         for (final Entry<String, AxStateOutput> soEntry : stateOutputs.entrySet()) {
301             soEntry.getValue().getKey().setParentReferenceKey(key);
302         }
303
304         for (final Entry<String, AxStateFinalizerLogic> sflEntry : stateFinalizerLogicMap.entrySet()) {
305             sflEntry.getValue().getKey().setParentReferenceKey(key);
306         }
307
308         for (final Entry<AxArtifactKey, AxStateTaskReference> trEntry : taskReferenceMap.entrySet()) {
309             trEntry.getValue().getKey().setParentReferenceKey(key);
310         }
311     }
312
313     /**
314      * Gets the names of all the states that this state may pass control to.
315      *
316      * @return the list of possible states that may receive control when this state completes execution
317      */
318     public Set<String> getNextStateSet() {
319         final Set<String> nextStateSet = new TreeSet<>();
320
321         for (final AxStateOutput stateOutput : stateOutputs.values()) {
322             nextStateSet.add(stateOutput.getNextState().getLocalName());
323         }
324         return nextStateSet;
325     }
326
327     /**
328      * {@inheritDoc}.
329      */
330     @Override
331     public AxReferenceKey getKey() {
332         return key;
333     }
334
335     /**
336      * {@inheritDoc}.
337      */
338     @Override
339     public List<AxKey> getKeys() {
340         final List<AxKey> keyList = key.getKeys();
341         keyList.add(new AxKeyUse(trigger.getKey()));
342         for (final AxStateOutput stateOutput : stateOutputs.values()) {
343             keyList.addAll(stateOutput.getKeys());
344         }
345         for (final AxArtifactKey contextAlbumReferenceKey : contextAlbumReferenceSet) {
346             keyList.add(new AxKeyUse(contextAlbumReferenceKey));
347         }
348         if (!taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey())) {
349             keyList.addAll(taskSelectionLogic.getKeys());
350         }
351         for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
352             keyList.addAll(stateFinalizerLogicEntry.getValue().getKeys());
353         }
354         keyList.add(new AxKeyUse(defaultTask.getKey()));
355         for (final Entry<AxArtifactKey, AxStateTaskReference> taskReferenceEntry : taskReferenceMap.entrySet()) {
356             keyList.add(new AxKeyUse(taskReferenceEntry.getKey()));
357
358             // A state output is allowed to be used more than once but we only return one usage as a
359             // key
360             for (AxKey referencedKey : taskReferenceEntry.getValue().getKeys()) {
361                 if (keyList.contains(referencedKey)) {
362                     keyList.add(referencedKey);
363                 }
364             }
365         }
366         return keyList;
367     }
368
369     /**
370      * Sets the reference key of the state.
371      *
372      * @param key the state reference key
373      */
374     public void setKey(final AxReferenceKey key) {
375         Assertions.argumentNotNull(key, "key may not be null");
376         this.key = key;
377     }
378
379     /**
380      * Gets the event that triggers the state.
381      *
382      * @return the event that triggers the state
383      */
384     public AxArtifactKey getTrigger() {
385         return trigger;
386     }
387
388     /**
389      * Sets the event that triggers the state.
390      *
391      * @param trigger the event that triggers the state
392      */
393     public void setTrigger(final AxArtifactKey trigger) {
394         Assertions.argumentNotNull(trigger, "trigger may not be null");
395         this.trigger = trigger;
396     }
397
398     /**
399      * Gets the possible state outputs for the state.
400      *
401      * @return the the possible state outputs for the state
402      */
403     public Map<String, AxStateOutput> getStateOutputs() {
404         return stateOutputs;
405     }
406
407     /**
408      * Sets the the possible state outputs for the state.
409      *
410      * @param stateOutputs the the possible state outputs for the state
411      */
412     public void setStateOutputs(final Map<String, AxStateOutput> stateOutputs) {
413         Assertions.argumentNotNull(stateOutputs, "stateOutputs may not be null");
414         this.stateOutputs = stateOutputs;
415     }
416
417     /**
418      * Gets the context album reference set defines the context that may be used by Task Selection Logic and State
419      * Finalizer Logic in the state.
420      *
421      * @return the context album reference set defines the context that may be used by Task Selection Logic and State
422      *         Finalizer Logic in the state
423      */
424     public Set<AxArtifactKey> getContextAlbumReferences() {
425         return contextAlbumReferenceSet;
426     }
427
428     /**
429      * Sets the context album reference set defines the context that may be used by Task Selection Logic and State
430      * Finalizer Logic in the state.
431      *
432      * @param contextAlbumReferences the context album reference set defines the context that may be used by Task
433      *        Selection Logic and State Finalizer Logic in the state
434      */
435     public void setContextAlbumReferences(final Set<AxArtifactKey> contextAlbumReferences) {
436         Assertions.argumentNotNull(contextAlbumReferences, "contextAlbumReferenceSet may not be null");
437         this.contextAlbumReferenceSet = contextAlbumReferences;
438     }
439
440     /**
441      * Gets the task selection logic that selects the task a state executes in an execution cycle.
442      *
443      * @return the task selection logic that selects the task a state executes in an execution cycle
444      */
445     public AxTaskSelectionLogic getTaskSelectionLogic() {
446         return taskSelectionLogic;
447     }
448
449     /**
450      * Sets the task selection logic that selects the task a state executes in an execution cycle.
451      *
452      * @param taskSelectionLogic the task selection logic that selects the task a state executes in an execution cycle
453      */
454     public void setTaskSelectionLogic(final AxTaskSelectionLogic taskSelectionLogic) {
455         Assertions.argumentNotNull(taskSelectionLogic, "taskSelectionLogic may not be null");
456         this.taskSelectionLogic = taskSelectionLogic;
457     }
458
459     /**
460      * Check if task selection logic has been specified the state.
461      *
462      * @return true, if task selection logic has been specified
463      */
464     public boolean checkSetTaskSelectionLogic() {
465         return !taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey());
466     }
467
468     /**
469      * Gets the state finalizer logic instances that selects the state output to use after a task executes in a state
470      * execution cycle.
471      *
472      * @return the state finalizer logic instances that selects the state output to use after a task executes in a state
473      *         execution cycle
474      */
475     public Map<String, AxStateFinalizerLogic> getStateFinalizerLogicMap() {
476         return stateFinalizerLogicMap;
477     }
478
479     /**
480      * Sets the state finalizer logic instances that selects the state output to use after a task executes in a state
481      * execution cycle.
482      *
483      * @param stateFinalizerLogicMap the state finalizer logic instances that selects the state output to use after a
484      *        task executes in a state execution cycle
485      */
486     public void setStateFinalizerLogicMap(final Map<String, AxStateFinalizerLogic> stateFinalizerLogicMap) {
487         Assertions.argumentNotNull(stateFinalizerLogicMap, "stateFinalizerLogic may not be null");
488         this.stateFinalizerLogicMap = stateFinalizerLogicMap;
489     }
490
491     /**
492      * Gets the default task that will execute in a state if Task Selection Logic is not specified.
493      *
494      * @return the default task that will execute in a state if Task Selection Logic is not specified
495      */
496     public AxArtifactKey getDefaultTask() {
497         return defaultTask;
498     }
499
500     /**
501      * Sets the default task that will execute in a state if Task Selection Logic is not specified.
502      *
503      * @param defaultTask the default task that will execute in a state if Task Selection Logic is not specified
504      */
505     public void setDefaultTask(final AxArtifactKey defaultTask) {
506         Assertions.argumentNotNull(defaultTask, "defaultTask may not be null");
507         this.defaultTask = defaultTask;
508     }
509
510     /**
511      * Gets the task reference map that defines the tasks for the state and how the task outputs are handled.
512      *
513      * @return the task reference map that defines the tasks for the state and how the task outputs are handled
514      */
515     public Map<AxArtifactKey, AxStateTaskReference> getTaskReferences() {
516         return taskReferenceMap;
517     }
518
519     /**
520      * Sets the task reference map that defines the tasks for the state and how the task outputs are handled.
521      *
522      * @param taskReferences the task reference map that defines the tasks for the state and how the task outputs are
523      *        handled
524      */
525     public void setTaskReferences(final Map<AxArtifactKey, AxStateTaskReference> taskReferences) {
526         Assertions.argumentNotNull(taskReferences, "taskReferenceMap may not be null");
527         this.taskReferenceMap = taskReferences;
528     }
529
530     /**
531      * {@inheritDoc}.
532      */
533     @Override
534     public AxValidationResult validate(final AxValidationResult resultIn) {
535         AxValidationResult result = resultIn;
536
537         if (key.equals(AxReferenceKey.getNullKey())) {
538             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
539                             "key is a null key"));
540         }
541
542         result = key.validate(result);
543
544         if (trigger.equals(AxArtifactKey.getNullKey())) {
545             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
546                             "trigger is a null key: " + trigger));
547         }
548         result = trigger.validate(result);
549
550         if (stateOutputs.size() == 0) {
551             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
552                             "stateOutputs may not be empty"));
553         } else {
554             validateStateOutputs(result);
555         }
556
557         validateContextAlbumReferences(result);
558         result = validateTaskSelectionLogic(result);
559         validateStateFinalizerLogics(result);
560
561         if (defaultTask.equals(AxArtifactKey.getNullKey())) {
562             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
563                             "default task has a null key: " + defaultTask));
564         }
565         result = defaultTask.validate(result);
566
567         if (taskReferenceMap.size() == 0) {
568             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
569                             "taskReferenceMap may not be empty"));
570         } else {
571             validateStateTaskReferences(result);
572         }
573
574         return result;
575     }
576
577     /**
578      * Validate the state outputs of the state.
579      *
580      * @param result the validation result to append to
581      */
582     private void validateStateOutputs(AxValidationResult result) {
583         final Set<String> nextStateNameSet = new TreeSet<>();
584         for (final Entry<String, AxStateOutput> stateOutputEntry : stateOutputs.entrySet()) {
585             if (stateOutputEntry.getValue() == null) {
586                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
587                                 "null state output value found on state output " + stateOutputEntry.getKey()));
588             } else {
589                 if (!stateOutputEntry.getValue().getKey().getParentReferenceKey().equals(key)) {
590                     result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
591                                     "parent key on state output " + stateOutputEntry.getKey()
592                                                     + DOES_NOT_EQUAL_STATE_KEY));
593                 }
594
595                 if (stateOutputEntry.getValue().getNextState().getLocalName().equals(key.getLocalName())) {
596                     result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
597                                     "state output next state "
598                                                     + stateOutputEntry.getValue().getNextState().getLocalName()
599                                                     + " may not be this state"));
600
601                 }
602
603                 if (nextStateNameSet.contains(stateOutputEntry.getValue().getNextState().getLocalName())) {
604                     result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
605                                     "duplicate state output next state name "
606                                                     + stateOutputEntry.getValue().getNextState().getLocalName()
607                                                     + " found"));
608                 } else {
609                     nextStateNameSet.add(stateOutputEntry.getValue().getNextState().getLocalName());
610                 }
611                 result = stateOutputEntry.getValue().validate(result);
612             }
613         }
614     }
615
616     /**
617      * Validate the context album references of the state.
618      *
619      * @param result the validation result to append to
620      */
621     private void validateContextAlbumReferences(AxValidationResult result) {
622         for (final AxArtifactKey contextAlbumReference : contextAlbumReferenceSet) {
623             if (contextAlbumReference.equals(AxArtifactKey.getNullKey())) {
624                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
625                                 "key on context album reference entry " + contextAlbumReference.getKey()
626                                                 + " may not be the null key"));
627             }
628
629             result = contextAlbumReference.validate(result);
630         }
631     }
632
633     /**
634      * Validate the task selection logic of the state.
635      *
636      * @param result the validation result to append to
637      * @return the result of the validation
638      */
639     private AxValidationResult validateTaskSelectionLogic(AxValidationResult result) {
640         if (!taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey())) {
641             if (!taskSelectionLogic.getKey().getParentReferenceKey().equals(key)) {
642                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
643                                 "taskSelectionLogic key " + taskSelectionLogic.getKey().getId()
644                                                 + DOES_NOT_EQUAL_STATE_KEY));
645             }
646             result = taskSelectionLogic.validate(result);
647         }
648
649         return result;
650     }
651
652     /**
653      * Validate all the state finalizer logic of the state.
654      *
655      * @param result the validation result to append to
656      */
657     private void validateStateFinalizerLogics(AxValidationResult result) {
658         for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
659             if (stateFinalizerLogicEntry.getValue() == null) {
660                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
661                                 "null state finalizer logic value found on state finalizer entry "
662                                                 + stateFinalizerLogicEntry.getKey()));
663             } else {
664                 if (!stateFinalizerLogicEntry.getValue().getKey().getParentReferenceKey().equals(key)) {
665                     result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
666                                     "stateFinalizerLogic parent key "
667                                                     + stateFinalizerLogicEntry.getValue().getKey().getId()
668                                                     + DOES_NOT_EQUAL_STATE_KEY));
669                 }
670
671                 result = stateFinalizerLogicEntry.getValue().validate(result);
672             }
673         }
674     }
675
676     /**
677      * Validate the tasks used the state.
678      *
679      * @param result the validation result to append to
680      */
681     private void validateStateTaskReferences(AxValidationResult result) {
682         final Set<String> usedStateOutputNameSet = new TreeSet<>();
683         final Set<String> usedStateFinalizerLogicNameSet = new TreeSet<>();
684
685         for (final Entry<AxArtifactKey, AxStateTaskReference> taskRefEntry : taskReferenceMap.entrySet()) {
686             if (taskRefEntry.getKey().equals(AxArtifactKey.getNullKey())) {
687                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
688                                 "task has a null key: " + taskRefEntry.getKey()));
689             }
690             result = taskRefEntry.getKey().validate(result);
691
692             if (taskRefEntry.getValue() == null) {
693                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
694                                 "null task reference value found on task reference " + taskRefEntry.getKey()));
695             } else {
696                 result = validateStateTaskReference(taskRefEntry.getKey(), taskRefEntry.getValue(),
697                                 usedStateOutputNameSet, usedStateFinalizerLogicNameSet, result);
698             }
699         }
700
701         final Set<String> unUsedStateOutputNameSet = new TreeSet<>(stateOutputs.keySet());
702         unUsedStateOutputNameSet.removeAll(usedStateOutputNameSet);
703         for (final String unUsedStateOutputName : unUsedStateOutputNameSet) {
704             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
705                             "state output " + unUsedStateOutputName + " is not used directly by any task"));
706         }
707
708         final Set<String> usnUedStateFinalizerLogicNameSet = new TreeSet<>(stateFinalizerLogicMap.keySet());
709         usnUedStateFinalizerLogicNameSet.removeAll(usedStateFinalizerLogicNameSet);
710         for (final String unusedStateFinalizerLogicName : usnUedStateFinalizerLogicNameSet) {
711             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
712                             "state finalizer logic " + unusedStateFinalizerLogicName + " is not used by any task"));
713         }
714
715         if (!taskReferenceMap.containsKey(defaultTask)) {
716             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
717                             "defaultTask " + defaultTask + " not found in taskReferenceMap"));
718         }
719     }
720
721     /**
722      * Validate the references of a task used in a state.
723      *
724      * @param taskKey The key of the task
725      * @param taskReference the task reference of the task
726      * @param stateOutputNameSet State outputs that have been used so far, will be appended for this task reference
727      * @param stateFinalizerLogicNameSet State finalizers that have been used so far, may be appended if this task
728      *        reference uses a finalzier
729      * @param result the validation result to append to
730      * @return the result of the validation
731      */
732     private AxValidationResult validateStateTaskReference(final AxArtifactKey taskKey,
733                     final AxStateTaskReference taskReference, Set<String> stateOutputNameSet,
734                     Set<String> stateFinalizerLogicNameSet, AxValidationResult result) {
735         if (!taskReference.getKey().getParentReferenceKey().equals(key)) {
736             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
737                             "stateTaskReference parent key " + taskReference.getKey().getId()
738                                             + DOES_NOT_EQUAL_STATE_KEY));
739         }
740
741         if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.DIRECT)) {
742             if (stateOutputs.containsKey(taskReference.getOutput().getLocalName())) {
743                 stateOutputNameSet.add(taskReference.getOutput().getLocalName());
744             } else {
745                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
746                                 "state output for task " + taskKey + " not found in stateOutputs"));
747             }
748         } else if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.LOGIC)) {
749             if (stateFinalizerLogicMap.containsKey(taskReference.getOutput().getLocalName())) {
750                 stateFinalizerLogicNameSet.add(taskReference.getOutput().getLocalName());
751             } else {
752                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
753                                 "state finalizer logic for task " + taskKey + " not found in stateFinalizerLogicMap"));
754             }
755         } else {
756             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
757                             "stateTaskReference task output type " + taskReference.getStateTaskOutputType()
758                                             + " is invalid"));
759         }
760
761         return taskReference.validate(result);
762     }
763
764     /**
765      * {@inheritDoc}.
766      */
767     @Override
768     public void clean() {
769         key.clean();
770         trigger.clean();
771         for (final AxStateOutput stateOutput : stateOutputs.values()) {
772             stateOutput.clean();
773         }
774         for (final AxArtifactKey contextAlbumReference : contextAlbumReferenceSet) {
775             contextAlbumReference.clean();
776         }
777         taskSelectionLogic.clean();
778         for (final AxStateFinalizerLogic stateFinalizerLogic : stateFinalizerLogicMap.values()) {
779             stateFinalizerLogic.clean();
780         }
781         defaultTask.clean();
782         for (final AxStateTaskReference taskReference : taskReferenceMap.values()) {
783             taskReference.clean();
784         }
785     }
786
787     /**
788      * {@inheritDoc}.
789      */
790     @Override
791     public String toString() {
792         final StringBuilder builder = new StringBuilder();
793         builder.append(this.getClass().getSimpleName());
794         builder.append(":(");
795         builder.append("stateKey=");
796         builder.append(key);
797         builder.append(",trigger=");
798         builder.append(trigger);
799         builder.append(",stateOutputs=");
800         builder.append(stateOutputs);
801         builder.append(",contextAlbumReferenceSet=");
802         builder.append(contextAlbumReferenceSet);
803         builder.append(",taskSelectionLogic=");
804         builder.append(taskSelectionLogic);
805         builder.append(",stateFinalizerLogicSet=");
806         builder.append(stateFinalizerLogicMap);
807         builder.append(",defaultTask=");
808         builder.append(defaultTask);
809         builder.append(",taskReferenceMap=");
810         builder.append(taskReferenceMap);
811         builder.append(")");
812         return builder.toString();
813     }
814
815     /**
816      * {@inheritDoc}.
817      */
818     @Override
819     public AxConcept copyTo(final AxConcept targetObject) {
820         Assertions.argumentNotNull(targetObject, "target may not be null");
821
822         final Object copyObject = targetObject;
823         Assertions.instanceOf(copyObject, AxState.class);
824
825         final AxState copy = ((AxState) copyObject);
826         copy.setKey(new AxReferenceKey(key));
827         copy.setTrigger(new AxArtifactKey(trigger));
828
829         final Map<String, AxStateOutput> newStateOutputs = new TreeMap<>();
830         for (final Entry<String, AxStateOutput> stateOutputEntry : stateOutputs.entrySet()) {
831             newStateOutputs.put(stateOutputEntry.getKey(), new AxStateOutput(stateOutputEntry.getValue()));
832         }
833         copy.setStateOutputs(newStateOutputs);
834
835         final Set<AxArtifactKey> newContextUsage = new TreeSet<>();
836         for (final AxArtifactKey contextAlbumReferenceItem : contextAlbumReferenceSet) {
837             newContextUsage.add(new AxArtifactKey(contextAlbumReferenceItem));
838         }
839         copy.setContextAlbumReferences(newContextUsage);
840
841         copy.setTaskSelectionLogic(new AxTaskSelectionLogic(taskSelectionLogic));
842
843         final Map<String, AxStateFinalizerLogic> newStateFinalizerLogicMap = new TreeMap<>();
844         for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
845             newStateFinalizerLogicMap.put(stateFinalizerLogicEntry.getKey(),
846                             new AxStateFinalizerLogic(stateFinalizerLogicEntry.getValue()));
847         }
848         copy.setStateFinalizerLogicMap(newStateFinalizerLogicMap);
849
850         copy.setDefaultTask(new AxArtifactKey(defaultTask));
851
852         final Map<AxArtifactKey, AxStateTaskReference> newTaskReferenceMap = new TreeMap<>();
853         for (final Entry<AxArtifactKey, AxStateTaskReference> taskReferenceEntry : taskReferenceMap.entrySet()) {
854             newTaskReferenceMap.put(new AxArtifactKey(taskReferenceEntry.getKey()),
855                             new AxStateTaskReference(taskReferenceEntry.getValue()));
856         }
857         copy.setTaskReferences(newTaskReferenceMap);
858
859         return copy;
860     }
861
862     /**
863      * {@inheritDoc}.
864      */
865     @Override
866     public int hashCode() {
867         final int prime = 31;
868         int result = 1;
869         result = prime * result + key.hashCode();
870         result = prime * result + trigger.hashCode();
871         result = prime * result + stateOutputs.hashCode();
872         result = prime * result + contextAlbumReferenceSet.hashCode();
873         result = prime * result + taskSelectionLogic.hashCode();
874         result = prime * result + stateFinalizerLogicMap.hashCode();
875         result = prime * result + defaultTask.hashCode();
876         result = prime * result + taskReferenceMap.hashCode();
877         return result;
878     }
879
880     /**
881      * {@inheritDoc}.
882      */
883     @Override
884     public boolean equals(final Object obj) {
885         if (obj == null) {
886             return false;
887         }
888         if (this == obj) {
889             return true;
890         }
891
892         if (getClass() != obj.getClass()) {
893             return false;
894         }
895
896         final AxState other = (AxState) obj;
897         if (!key.equals(other.key)) {
898             return false;
899         }
900         if (!trigger.equals(other.trigger)) {
901             return false;
902         }
903         if (!stateOutputs.equals(other.stateOutputs)) {
904             return false;
905         }
906         if (!contextAlbumReferenceSet.equals(other.contextAlbumReferenceSet)) {
907             return false;
908         }
909         if (!taskSelectionLogic.equals(other.taskSelectionLogic)) {
910             return false;
911         }
912         if (!stateFinalizerLogicMap.equals(other.stateFinalizerLogicMap)) {
913             return false;
914         }
915         if (!defaultTask.equals(other.defaultTask)) {
916             return false;
917         }
918         return taskReferenceMap.equals(other.taskReferenceMap);
919     }
920
921     /**
922      * {@inheritDoc}.
923      */
924     @Override
925     public int compareTo(final AxConcept otherObj) {
926         if (otherObj == null) {
927             return -1;
928         }
929         if (this == otherObj) {
930             return 0;
931         }
932         if (getClass() != otherObj.getClass()) {
933             return this.hashCode() - otherObj.hashCode();
934         }
935
936         return compareObjectFields((AxState) otherObj);
937     }
938
939     /**
940      * Compare the object fields on this state to another state.
941      *
942      * @param the other state to compare with
943      * @return the result of the comparison
944      */
945     private int compareObjectFields(final AxState other) {
946         if (!key.equals(other.key)) {
947             return key.compareTo(other.key);
948         }
949         if (!trigger.equals(other.trigger)) {
950             return trigger.compareTo(other.trigger);
951         }
952         if (!stateOutputs.equals(other.stateOutputs)) {
953             return stateOutputs.hashCode() - other.stateOutputs.hashCode();
954         }
955         if (!contextAlbumReferenceSet.equals(other.contextAlbumReferenceSet)) {
956             return (contextAlbumReferenceSet.hashCode() - other.contextAlbumReferenceSet.hashCode());
957         }
958         if (!taskSelectionLogic.equals(other.taskSelectionLogic)) {
959             return taskSelectionLogic.compareTo(other.taskSelectionLogic);
960         }
961         if (!stateFinalizerLogicMap.equals(other.stateFinalizerLogicMap)) {
962             return stateFinalizerLogicMap.hashCode() - other.stateFinalizerLogicMap.hashCode();
963         }
964         if (!defaultTask.equals(other.defaultTask)) {
965             return defaultTask.compareTo(other.defaultTask);
966         }
967         if (!taskReferenceMap.equals(other.taskReferenceMap)) {
968             return (taskReferenceMap.hashCode() - other.taskReferenceMap.hashCode());
969         }
970
971         return 0;
972     }
973 }