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