Use Assertions class from policy-common
[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      * (non-Javadoc)
329      *
330      * @see org.onap.policy.apex.model.basicmodel.concepts.AxConcept#getKey()
331      */
332     @Override
333     public AxReferenceKey getKey() {
334         return key;
335     }
336
337     /*
338      * (non-Javadoc)
339      *
340      * @see org.onap.policy.apex.model.basicmodel.concepts.AxConcept#getKeys()
341      */
342     @Override
343     public List<AxKey> getKeys() {
344         final List<AxKey> keyList = key.getKeys();
345         keyList.add(new AxKeyUse(trigger.getKey()));
346         for (final AxStateOutput stateOutput : stateOutputs.values()) {
347             keyList.addAll(stateOutput.getKeys());
348         }
349         for (final AxArtifactKey contextAlbumReferenceKey : contextAlbumReferenceSet) {
350             keyList.add(new AxKeyUse(contextAlbumReferenceKey));
351         }
352         if (!taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey())) {
353             keyList.addAll(taskSelectionLogic.getKeys());
354         }
355         for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
356             keyList.addAll(stateFinalizerLogicEntry.getValue().getKeys());
357         }
358         keyList.add(new AxKeyUse(defaultTask.getKey()));
359         for (final Entry<AxArtifactKey, AxStateTaskReference> taskReferenceEntry : taskReferenceMap.entrySet()) {
360             keyList.add(new AxKeyUse(taskReferenceEntry.getKey()));
361
362             // A state output is allowed to be used more than once but we only return one usage as a
363             // key
364             for (AxKey referencedKey : taskReferenceEntry.getValue().getKeys()) {
365                 if (keyList.contains(referencedKey)) {
366                     keyList.add(referencedKey);
367                 }
368             }
369         }
370         return keyList;
371     }
372
373     /**
374      * Sets the reference key of the state.
375      *
376      * @param key the state reference key
377      */
378     public void setKey(final AxReferenceKey key) {
379         Assertions.argumentNotNull(key, "key may not be null");
380         this.key = key;
381     }
382
383     /**
384      * Gets the event that triggers the state.
385      *
386      * @return the event that triggers the state
387      */
388     public AxArtifactKey getTrigger() {
389         return trigger;
390     }
391
392     /**
393      * Sets the event that triggers the state.
394      *
395      * @param trigger the event that triggers the state
396      */
397     public void setTrigger(final AxArtifactKey trigger) {
398         Assertions.argumentNotNull(trigger, "trigger may not be null");
399         this.trigger = trigger;
400     }
401
402     /**
403      * Gets the possible state outputs for the state.
404      *
405      * @return the the possible state outputs for the state
406      */
407     public Map<String, AxStateOutput> getStateOutputs() {
408         return stateOutputs;
409     }
410
411     /**
412      * Sets the the possible state outputs for the state.
413      *
414      * @param stateOutputs the the possible state outputs for the state
415      */
416     public void setStateOutputs(final Map<String, AxStateOutput> stateOutputs) {
417         Assertions.argumentNotNull(stateOutputs, "stateOutputs may not be null");
418         this.stateOutputs = stateOutputs;
419     }
420
421     /**
422      * Gets the context album reference set defines the context that may be used by Task Selection Logic and State
423      * Finalizer Logic in the state.
424      *
425      * @return the context album reference set defines the context that may be used by Task Selection Logic and State
426      *         Finalizer Logic in the state
427      */
428     public Set<AxArtifactKey> getContextAlbumReferences() {
429         return contextAlbumReferenceSet;
430     }
431
432     /**
433      * Sets the context album reference set defines the context that may be used by Task Selection Logic and State
434      * Finalizer Logic in the state.
435      *
436      * @param contextAlbumReferences the context album reference set defines the context that may be used by Task
437      *        Selection Logic and State Finalizer Logic in the state
438      */
439     public void setContextAlbumReferences(final Set<AxArtifactKey> contextAlbumReferences) {
440         Assertions.argumentNotNull(contextAlbumReferences, "contextAlbumReferenceSet may not be null");
441         this.contextAlbumReferenceSet = contextAlbumReferences;
442     }
443
444     /**
445      * Gets the task selection logic that selects the task a state executes in an execution cycle.
446      *
447      * @return the task selection logic that selects the task a state executes in an execution cycle
448      */
449     public AxTaskSelectionLogic getTaskSelectionLogic() {
450         return taskSelectionLogic;
451     }
452
453     /**
454      * Sets the task selection logic that selects the task a state executes in an execution cycle.
455      *
456      * @param taskSelectionLogic the task selection logic that selects the task a state executes in an execution cycle
457      */
458     public void setTaskSelectionLogic(final AxTaskSelectionLogic taskSelectionLogic) {
459         Assertions.argumentNotNull(taskSelectionLogic, "taskSelectionLogic may not be null");
460         this.taskSelectionLogic = taskSelectionLogic;
461     }
462
463     /**
464      * Check if task selection logic has been specified the state.
465      *
466      * @return true, if task selection logic has been specified
467      */
468     public boolean checkSetTaskSelectionLogic() {
469         return !taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey());
470     }
471
472     /**
473      * Gets the state finalizer logic instances that selects the state output to use after a task executes in a state
474      * execution cycle.
475      *
476      * @return the state finalizer logic instances that selects the state output to use after a task executes in a state
477      *         execution cycle
478      */
479     public Map<String, AxStateFinalizerLogic> getStateFinalizerLogicMap() {
480         return stateFinalizerLogicMap;
481     }
482
483     /**
484      * Sets the state finalizer logic instances that selects the state output to use after a task executes in a state
485      * execution cycle.
486      *
487      * @param stateFinalizerLogicMap the state finalizer logic instances that selects the state output to use after a
488      *        task executes in a state execution cycle
489      */
490     public void setStateFinalizerLogicMap(final Map<String, AxStateFinalizerLogic> stateFinalizerLogicMap) {
491         Assertions.argumentNotNull(stateFinalizerLogicMap, "stateFinalizerLogic may not be null");
492         this.stateFinalizerLogicMap = stateFinalizerLogicMap;
493     }
494
495     /**
496      * Gets the default task that will execute in a state if Task Selection Logic is not specified.
497      *
498      * @return the default task that will execute in a state if Task Selection Logic is not specified
499      */
500     public AxArtifactKey getDefaultTask() {
501         return defaultTask;
502     }
503
504     /**
505      * Sets the default task that will execute in a state if Task Selection Logic is not specified.
506      *
507      * @param defaultTask the default task that will execute in a state if Task Selection Logic is not specified
508      */
509     public void setDefaultTask(final AxArtifactKey defaultTask) {
510         Assertions.argumentNotNull(defaultTask, "defaultTask may not be null");
511         this.defaultTask = defaultTask;
512     }
513
514     /**
515      * Gets the task reference map that defines the tasks for the state and how the task outputs are handled.
516      *
517      * @return the task reference map that defines the tasks for the state and how the task outputs are handled
518      */
519     public Map<AxArtifactKey, AxStateTaskReference> getTaskReferences() {
520         return taskReferenceMap;
521     }
522
523     /**
524      * Sets the task reference map that defines the tasks for the state and how the task outputs are handled.
525      *
526      * @param taskReferences the task reference map that defines the tasks for the state and how the task outputs are
527      *        handled
528      */
529     public void setTaskReferences(final Map<AxArtifactKey, AxStateTaskReference> taskReferences) {
530         Assertions.argumentNotNull(taskReferences, "taskReferenceMap may not be null");
531         this.taskReferenceMap = taskReferences;
532     }
533
534     /*
535      * (non-Javadoc)
536      *
537      * @see org.onap.policy.apex.model.basicmodel.concepts.AxConcept#validate(org.onap.policy.apex.model.
538      * basicmodel.concepts.AxValidationResult)
539      */
540     @Override
541     public AxValidationResult validate(final AxValidationResult resultIn) {
542         AxValidationResult result = resultIn;
543
544         if (key.equals(AxReferenceKey.getNullKey())) {
545             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
546                             "key is a null key"));
547         }
548
549         result = key.validate(result);
550
551         if (trigger.equals(AxArtifactKey.getNullKey())) {
552             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
553                             "trigger is a null key: " + trigger));
554         }
555         result = trigger.validate(result);
556
557         if (stateOutputs.size() == 0) {
558             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
559                             "stateOutputs may not be empty"));
560         } else {
561             validateStateOutputs(result);
562         }
563
564         validateContextAlbumReferences(result);
565         result = validateTaskSelectionLogic(result);
566         validateStateFinalizerLogics(result);
567
568         if (defaultTask.equals(AxArtifactKey.getNullKey())) {
569             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
570                             "default task has a null key: " + defaultTask));
571         }
572         result = defaultTask.validate(result);
573
574         if (taskReferenceMap.size() == 0) {
575             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
576                             "taskReferenceMap may not be empty"));
577         } else {
578             validateStateTaskReferences(result);
579         }
580
581         return result;
582     }
583
584     /**
585      * Validate the state outputs of the state.
586      *
587      * @param result the validation result to append to
588      */
589     private void validateStateOutputs(AxValidationResult result) {
590         final Set<String> nextStateNameSet = new TreeSet<>();
591         for (final Entry<String, AxStateOutput> stateOutputEntry : stateOutputs.entrySet()) {
592             if (stateOutputEntry.getValue() == null) {
593                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
594                                 "null state output value found on state output " + stateOutputEntry.getKey()));
595             } else {
596                 if (!stateOutputEntry.getValue().getKey().getParentReferenceKey().equals(key)) {
597                     result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
598                                     "parent key on state output " + stateOutputEntry.getKey()
599                                                     + DOES_NOT_EQUAL_STATE_KEY));
600                 }
601
602                 if (stateOutputEntry.getValue().getNextState().getLocalName().equals(key.getLocalName())) {
603                     result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
604                                     "state output next state "
605                                                     + stateOutputEntry.getValue().getNextState().getLocalName()
606                                                     + " may not be this state"));
607
608                 }
609
610                 if (nextStateNameSet.contains(stateOutputEntry.getValue().getNextState().getLocalName())) {
611                     result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
612                                     "duplicate state output next state name "
613                                                     + stateOutputEntry.getValue().getNextState().getLocalName()
614                                                     + " found"));
615                 } else {
616                     nextStateNameSet.add(stateOutputEntry.getValue().getNextState().getLocalName());
617                 }
618                 result = stateOutputEntry.getValue().validate(result);
619             }
620         }
621     }
622
623     /**
624      * Validate the context album references of the state.
625      *
626      * @param result the validation result to append to
627      */
628     private void validateContextAlbumReferences(AxValidationResult result) {
629         for (final AxArtifactKey contextAlbumReference : contextAlbumReferenceSet) {
630             if (contextAlbumReference.equals(AxArtifactKey.getNullKey())) {
631                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
632                                 "key on context album reference entry " + contextAlbumReference.getKey()
633                                                 + " may not be the null key"));
634             }
635
636             result = contextAlbumReference.validate(result);
637         }
638     }
639
640     /**
641      * Validate the task selection logic of the state.
642      *
643      * @param result the validation result to append to
644      * @return the result of the validation
645      */
646     private AxValidationResult validateTaskSelectionLogic(AxValidationResult result) {
647         if (!taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey())) {
648             if (!taskSelectionLogic.getKey().getParentReferenceKey().equals(key)) {
649                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
650                                 "taskSelectionLogic key " + taskSelectionLogic.getKey().getId()
651                                                 + DOES_NOT_EQUAL_STATE_KEY));
652             }
653             result = taskSelectionLogic.validate(result);
654         }
655
656         return result;
657     }
658
659     /**
660      * Validate all the state finalizer logic of the state.
661      *
662      * @param result the validation result to append to
663      */
664     private void validateStateFinalizerLogics(AxValidationResult result) {
665         for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
666             if (stateFinalizerLogicEntry.getValue() == null) {
667                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
668                                 "null state finalizer logic value found on state finalizer entry "
669                                                 + stateFinalizerLogicEntry.getKey()));
670             } else {
671                 if (!stateFinalizerLogicEntry.getValue().getKey().getParentReferenceKey().equals(key)) {
672                     result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
673                                     "stateFinalizerLogic parent key "
674                                                     + stateFinalizerLogicEntry.getValue().getKey().getId()
675                                                     + DOES_NOT_EQUAL_STATE_KEY));
676                 }
677
678                 result = stateFinalizerLogicEntry.getValue().validate(result);
679             }
680         }
681     }
682
683     /**
684      * Validate the tasks used the state.
685      *
686      * @param result the validation result to append to
687      */
688     private void validateStateTaskReferences(AxValidationResult result) {
689         final Set<String> usedStateOutputNameSet = new TreeSet<>();
690         final Set<String> usedStateFinalizerLogicNameSet = new TreeSet<>();
691
692         for (final Entry<AxArtifactKey, AxStateTaskReference> taskRefEntry : taskReferenceMap.entrySet()) {
693             if (taskRefEntry.getKey().equals(AxArtifactKey.getNullKey())) {
694                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
695                                 "task has a null key: " + taskRefEntry.getKey()));
696             }
697             result = taskRefEntry.getKey().validate(result);
698
699             if (taskRefEntry.getValue() == null) {
700                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
701                                 "null task reference value found on task reference " + taskRefEntry.getKey()));
702             } else {
703                 result = validateStateTaskReference(taskRefEntry.getKey(), taskRefEntry.getValue(),
704                                 usedStateOutputNameSet, usedStateFinalizerLogicNameSet, result);
705             }
706         }
707
708         final Set<String> unUsedStateOutputNameSet = new TreeSet<>(stateOutputs.keySet());
709         unUsedStateOutputNameSet.removeAll(usedStateOutputNameSet);
710         for (final String unUsedStateOutputName : unUsedStateOutputNameSet) {
711             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
712                             "state output " + unUsedStateOutputName + " is not used directly by any task"));
713         }
714
715         final Set<String> usnUedStateFinalizerLogicNameSet = new TreeSet<>(stateFinalizerLogicMap.keySet());
716         usnUedStateFinalizerLogicNameSet.removeAll(usedStateFinalizerLogicNameSet);
717         for (final String unusedStateFinalizerLogicName : usnUedStateFinalizerLogicNameSet) {
718             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
719                             "state finalizer logic " + unusedStateFinalizerLogicName + " is not used by any task"));
720         }
721
722         if (!taskReferenceMap.containsKey(defaultTask)) {
723             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
724                             "defaultTask " + defaultTask + " not found in taskReferenceMap"));
725         }
726     }
727
728     /**
729      * Validate the references of a task used in a state.
730      *
731      * @param taskKey The key of the task
732      * @param taskReference the task reference of the task
733      * @param stateOutputNameSet State outputs that have been used so far, will be appended for this task reference
734      * @param stateFinalizerLogicNameSet State finalizers that have been used so far, may be appended if this task
735      *        reference uses a finalzier
736      * @param result the validation result to append to
737      * @return the result of the validation
738      */
739     private AxValidationResult validateStateTaskReference(final AxArtifactKey taskKey,
740                     final AxStateTaskReference taskReference, Set<String> stateOutputNameSet,
741                     Set<String> stateFinalizerLogicNameSet, AxValidationResult result) {
742         if (!taskReference.getKey().getParentReferenceKey().equals(key)) {
743             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
744                             "stateTaskReference parent key " + taskReference.getKey().getId()
745                                             + DOES_NOT_EQUAL_STATE_KEY));
746         }
747
748         if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.DIRECT)) {
749             if (stateOutputs.containsKey(taskReference.getOutput().getLocalName())) {
750                 stateOutputNameSet.add(taskReference.getOutput().getLocalName());
751             } else {
752                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
753                                 "state output for task " + taskKey + " not found in stateOutputs"));
754             }
755         } else if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.LOGIC)) {
756             if (stateFinalizerLogicMap.containsKey(taskReference.getOutput().getLocalName())) {
757                 stateFinalizerLogicNameSet.add(taskReference.getOutput().getLocalName());
758             } else {
759                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
760                                 "state finalizer logic for task " + taskKey + " not found in stateFinalizerLogicMap"));
761             }
762         } else {
763             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
764                             "stateTaskReference task output type " + taskReference.getStateTaskOutputType()
765                                             + " is invalid"));
766         }
767
768         return taskReference.validate(result);
769     }
770
771     /*
772      * (non-Javadoc)
773      *
774      * @see org.onap.policy.apex.model.basicmodel.concepts.AxConcept#clean()
775      */
776     @Override
777     public void clean() {
778         key.clean();
779         trigger.clean();
780         for (final AxStateOutput stateOutput : stateOutputs.values()) {
781             stateOutput.clean();
782         }
783         for (final AxArtifactKey contextAlbumReference : contextAlbumReferenceSet) {
784             contextAlbumReference.clean();
785         }
786         taskSelectionLogic.clean();
787         for (final AxStateFinalizerLogic stateFinalizerLogic : stateFinalizerLogicMap.values()) {
788             stateFinalizerLogic.clean();
789         }
790         defaultTask.clean();
791         for (final AxStateTaskReference taskReference : taskReferenceMap.values()) {
792             taskReference.clean();
793         }
794     }
795
796     /*
797      * (non-Javadoc)
798      *
799      * @see org.onap.policy.apex.model.basicmodel.concepts.AxConcept#toString()
800      */
801     @Override
802     public String toString() {
803         final StringBuilder builder = new StringBuilder();
804         builder.append(this.getClass().getSimpleName());
805         builder.append(":(");
806         builder.append("stateKey=");
807         builder.append(key);
808         builder.append(",trigger=");
809         builder.append(trigger);
810         builder.append(",stateOutputs=");
811         builder.append(stateOutputs);
812         builder.append(",contextAlbumReferenceSet=");
813         builder.append(contextAlbumReferenceSet);
814         builder.append(",taskSelectionLogic=");
815         builder.append(taskSelectionLogic);
816         builder.append(",stateFinalizerLogicSet=");
817         builder.append(stateFinalizerLogicMap);
818         builder.append(",defaultTask=");
819         builder.append(defaultTask);
820         builder.append(",taskReferenceMap=");
821         builder.append(taskReferenceMap);
822         builder.append(")");
823         return builder.toString();
824     }
825
826     /*
827      * (non-Javadoc)
828      *
829      * @see org.onap.policy.apex.model.basicmodel.concepts.AxConcept#copyTo(org.onap.policy.apex.model.
830      * basicmodel.concepts.AxConcept)
831      */
832     @Override
833     public AxConcept copyTo(final AxConcept targetObject) {
834         Assertions.argumentNotNull(targetObject, "target may not be null");
835
836         final Object copyObject = targetObject;
837         Assertions.instanceOf(copyObject, AxState.class);
838
839         final AxState copy = ((AxState) copyObject);
840         copy.setKey(new AxReferenceKey(key));
841         copy.setTrigger(new AxArtifactKey(trigger));
842
843         final Map<String, AxStateOutput> newStateOutputs = new TreeMap<>();
844         for (final Entry<String, AxStateOutput> stateOutputEntry : stateOutputs.entrySet()) {
845             newStateOutputs.put(stateOutputEntry.getKey(), new AxStateOutput(stateOutputEntry.getValue()));
846         }
847         copy.setStateOutputs(newStateOutputs);
848
849         final Set<AxArtifactKey> newContextUsage = new TreeSet<>();
850         for (final AxArtifactKey contextAlbumReferenceItem : contextAlbumReferenceSet) {
851             newContextUsage.add(new AxArtifactKey(contextAlbumReferenceItem));
852         }
853         copy.setContextAlbumReferences(newContextUsage);
854
855         copy.setTaskSelectionLogic(new AxTaskSelectionLogic(taskSelectionLogic));
856
857         final Map<String, AxStateFinalizerLogic> newStateFinalizerLogicMap = new TreeMap<>();
858         for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
859             newStateFinalizerLogicMap.put(stateFinalizerLogicEntry.getKey(),
860                             new AxStateFinalizerLogic(stateFinalizerLogicEntry.getValue()));
861         }
862         copy.setStateFinalizerLogicMap(newStateFinalizerLogicMap);
863
864         copy.setDefaultTask(new AxArtifactKey(defaultTask));
865
866         final Map<AxArtifactKey, AxStateTaskReference> newTaskReferenceMap = new TreeMap<>();
867         for (final Entry<AxArtifactKey, AxStateTaskReference> taskReferenceEntry : taskReferenceMap.entrySet()) {
868             newTaskReferenceMap.put(new AxArtifactKey(taskReferenceEntry.getKey()),
869                             new AxStateTaskReference(taskReferenceEntry.getValue()));
870         }
871         copy.setTaskReferences(newTaskReferenceMap);
872
873         return copy;
874     }
875
876     /*
877      * (non-Javadoc)
878      *
879      * @see org.onap.policy.apex.model.basicmodel.concepts.AxConcept#hashCode()
880      */
881     @Override
882     public int hashCode() {
883         final int prime = 31;
884         int result = 1;
885         result = prime * result + key.hashCode();
886         result = prime * result + trigger.hashCode();
887         result = prime * result + stateOutputs.hashCode();
888         result = prime * result + contextAlbumReferenceSet.hashCode();
889         result = prime * result + taskSelectionLogic.hashCode();
890         result = prime * result + stateFinalizerLogicMap.hashCode();
891         result = prime * result + defaultTask.hashCode();
892         result = prime * result + taskReferenceMap.hashCode();
893         return result;
894     }
895
896     /*
897      * (non-Javadoc)
898      *
899      * @see org.onap.policy.apex.model.basicmodel.concepts.AxConcept#equals(java.lang.Object)
900      */
901     @Override
902     public boolean equals(final Object obj) {
903         if (obj == null) {
904             return false;
905         }
906         if (this == obj) {
907             return true;
908         }
909
910         if (getClass() != obj.getClass()) {
911             return false;
912         }
913
914         final AxState other = (AxState) obj;
915         if (!key.equals(other.key)) {
916             return false;
917         }
918         if (!trigger.equals(other.trigger)) {
919             return false;
920         }
921         if (!stateOutputs.equals(other.stateOutputs)) {
922             return false;
923         }
924         if (!contextAlbumReferenceSet.equals(other.contextAlbumReferenceSet)) {
925             return false;
926         }
927         if (!taskSelectionLogic.equals(other.taskSelectionLogic)) {
928             return false;
929         }
930         if (!stateFinalizerLogicMap.equals(other.stateFinalizerLogicMap)) {
931             return false;
932         }
933         if (!defaultTask.equals(other.defaultTask)) {
934             return false;
935         }
936         return taskReferenceMap.equals(other.taskReferenceMap);
937     }
938
939     /*
940      * (non-Javadoc)
941      *
942      * @see java.lang.Comparable#compareTo(java.lang.Object)
943      */
944     @Override
945     public int compareTo(final AxConcept otherObj) {
946         if (otherObj == null) {
947             return -1;
948         }
949         if (this == otherObj) {
950             return 0;
951         }
952         if (getClass() != otherObj.getClass()) {
953             return this.hashCode() - otherObj.hashCode();
954         }
955
956         return compareObjectFields((AxState) otherObj);
957     }
958
959     /**
960      * Compare the object fields on this state to another state.
961      *
962      * @param the other state to compare with
963      * @return the result of the comparison
964      */
965     private int compareObjectFields(final AxState other) {
966         if (!key.equals(other.key)) {
967             return key.compareTo(other.key);
968         }
969         if (!trigger.equals(other.trigger)) {
970             return trigger.compareTo(other.trigger);
971         }
972         if (!stateOutputs.equals(other.stateOutputs)) {
973             return stateOutputs.hashCode() - other.stateOutputs.hashCode();
974         }
975         if (!contextAlbumReferenceSet.equals(other.contextAlbumReferenceSet)) {
976             return (contextAlbumReferenceSet.hashCode() - other.contextAlbumReferenceSet.hashCode());
977         }
978         if (!taskSelectionLogic.equals(other.taskSelectionLogic)) {
979             return taskSelectionLogic.compareTo(other.taskSelectionLogic);
980         }
981         if (!stateFinalizerLogicMap.equals(other.stateFinalizerLogicMap)) {
982             return stateFinalizerLogicMap.hashCode() - other.stateFinalizerLogicMap.hashCode();
983         }
984         if (!defaultTask.equals(other.defaultTask)) {
985             return defaultTask.compareTo(other.defaultTask);
986         }
987         if (!taskReferenceMap.equals(other.taskReferenceMap)) {
988             return (taskReferenceMap.hashCode() - other.taskReferenceMap.hashCode());
989         }
990
991         return 0;
992     }
993 }