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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 * SPDX-License-Identifier: Apache-2.0
20 * ============LICENSE_END=========================================================
23 package org.onap.policy.apex.model.policymodel.concepts;
25 import java.util.List;
27 import java.util.Map.Entry;
29 import java.util.TreeMap;
30 import java.util.TreeSet;
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;
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;
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.
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.
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.
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.
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.
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
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}
126 @Table(name = "AxState")
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" })
134 public class AxState extends AxConcept {
135 private static final String DOES_NOT_EQUAL_STATE_KEY = " does not equal state key";
137 private static final long serialVersionUID = 8041771382337655535L;
140 @XmlElement(name = "stateKey", required = true)
141 private AxReferenceKey key;
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;
151 @ManyToMany(cascade = CascadeType.ALL)
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;
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;
173 @JoinTable(name = "STATE_TSL_JT",
175 @JoinColumn(name = "tslParentKeyName", referencedColumnName = "parentKeyName", updatable = 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;
186 @ManyToMany(cascade = CascadeType.ALL)
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;
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;
206 @ManyToMany(cascade = CascadeType.ALL)
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;
221 * The Default Constructor creates a state with a null reference key and with default values for all other fields.
224 this(new AxReferenceKey());
225 contextAlbumReferenceSet = new TreeSet<>();
226 taskReferenceMap = new TreeMap<>();
232 * @param copyConcept the concept to copy from
234 public AxState(final AxState copyConcept) {
239 * The Keyed Constructor creates a state with the given reference key and with default values for all other fields.
241 * @param key the reference key of the state
243 public AxState(final AxReferenceKey key) {
245 this(new AxStateParamsBuilder()
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
259 * This Constructor creates a state with all its fields defined.
261 * @param axStateParams parameters for state creation
263 // CHECKSTYLE:OFF: checkstyle:parameterNumber
264 public AxState(AxStateParamsBuilder axStateParams) {
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");
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();
285 // CHECKSTYLE:ON: checkstyle:parameterNumber
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.
292 * @param unmarshaler the unmarshaler that is unmarshaling the model
293 * @param parent the parent object of this object in the unmarshaler
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);
300 for (final Entry<String, AxStateOutput> soEntry : stateOutputs.entrySet()) {
301 soEntry.getValue().getKey().setParentReferenceKey(key);
304 for (final Entry<String, AxStateFinalizerLogic> sflEntry : stateFinalizerLogicMap.entrySet()) {
305 sflEntry.getValue().getKey().setParentReferenceKey(key);
308 for (final Entry<AxArtifactKey, AxStateTaskReference> trEntry : taskReferenceMap.entrySet()) {
309 trEntry.getValue().getKey().setParentReferenceKey(key);
314 * Gets the names of all the states that this state may pass control to.
316 * @return the list of possible states that may receive control when this state completes execution
318 public Set<String> getNextStateSet() {
319 final Set<String> nextStateSet = new TreeSet<>();
321 for (final AxStateOutput stateOutput : stateOutputs.values()) {
322 nextStateSet.add(stateOutput.getNextState().getLocalName());
331 public AxReferenceKey getKey() {
339 public List<AxKey> getKeys() {
340 final List<AxKey> keyList = key.getKeys();
341 keyList.add(new AxKeyUse(trigger.getKey()));
342 for (final AxStateOutput stateOutput : stateOutputs.values()) {
343 keyList.addAll(stateOutput.getKeys());
345 for (final AxArtifactKey contextAlbumReferenceKey : contextAlbumReferenceSet) {
346 keyList.add(new AxKeyUse(contextAlbumReferenceKey));
348 if (!taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey())) {
349 keyList.addAll(taskSelectionLogic.getKeys());
351 for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
352 keyList.addAll(stateFinalizerLogicEntry.getValue().getKeys());
354 keyList.add(new AxKeyUse(defaultTask.getKey()));
355 for (final Entry<AxArtifactKey, AxStateTaskReference> taskReferenceEntry : taskReferenceMap.entrySet()) {
356 keyList.add(new AxKeyUse(taskReferenceEntry.getKey()));
358 // A state output is allowed to be used more than once but we only return one usage as a
360 for (AxKey referencedKey : taskReferenceEntry.getValue().getKeys()) {
361 if (keyList.contains(referencedKey)) {
362 keyList.add(referencedKey);
370 * Sets the reference key of the state.
372 * @param key the state reference key
374 public void setKey(final AxReferenceKey key) {
375 Assertions.argumentNotNull(key, "key may not be null");
380 * Gets the event that triggers the state.
382 * @return the event that triggers the state
384 public AxArtifactKey getTrigger() {
389 * Sets the event that triggers the state.
391 * @param trigger the event that triggers the state
393 public void setTrigger(final AxArtifactKey trigger) {
394 Assertions.argumentNotNull(trigger, "trigger may not be null");
395 this.trigger = trigger;
399 * Gets the possible state outputs for the state.
401 * @return the the possible state outputs for the state
403 public Map<String, AxStateOutput> getStateOutputs() {
408 * Sets the the possible state outputs for the state.
410 * @param stateOutputs the the possible state outputs for the state
412 public void setStateOutputs(final Map<String, AxStateOutput> stateOutputs) {
413 Assertions.argumentNotNull(stateOutputs, "stateOutputs may not be null");
414 this.stateOutputs = stateOutputs;
418 * Gets the context album reference set defines the context that may be used by Task Selection Logic and State
419 * Finalizer Logic in the state.
421 * @return the context album reference set defines the context that may be used by Task Selection Logic and State
422 * Finalizer Logic in the state
424 public Set<AxArtifactKey> getContextAlbumReferences() {
425 return contextAlbumReferenceSet;
429 * Sets the context album reference set defines the context that may be used by Task Selection Logic and State
430 * Finalizer Logic in the state.
432 * @param contextAlbumReferences the context album reference set defines the context that may be used by Task
433 * Selection Logic and State Finalizer Logic in the state
435 public void setContextAlbumReferences(final Set<AxArtifactKey> contextAlbumReferences) {
436 Assertions.argumentNotNull(contextAlbumReferences, "contextAlbumReferenceSet may not be null");
437 this.contextAlbumReferenceSet = contextAlbumReferences;
441 * Gets the task selection logic that selects the task a state executes in an execution cycle.
443 * @return the task selection logic that selects the task a state executes in an execution cycle
445 public AxTaskSelectionLogic getTaskSelectionLogic() {
446 return taskSelectionLogic;
450 * Sets the task selection logic that selects the task a state executes in an execution cycle.
452 * @param taskSelectionLogic the task selection logic that selects the task a state executes in an execution cycle
454 public void setTaskSelectionLogic(final AxTaskSelectionLogic taskSelectionLogic) {
455 Assertions.argumentNotNull(taskSelectionLogic, "taskSelectionLogic may not be null");
456 this.taskSelectionLogic = taskSelectionLogic;
460 * Check if task selection logic has been specified the state.
462 * @return true, if task selection logic has been specified
464 public boolean checkSetTaskSelectionLogic() {
465 return !taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey());
469 * Gets the state finalizer logic instances that selects the state output to use after a task executes in a state
472 * @return the state finalizer logic instances that selects the state output to use after a task executes in a state
475 public Map<String, AxStateFinalizerLogic> getStateFinalizerLogicMap() {
476 return stateFinalizerLogicMap;
480 * Sets the state finalizer logic instances that selects the state output to use after a task executes in a state
483 * @param stateFinalizerLogicMap the state finalizer logic instances that selects the state output to use after a
484 * task executes in a state execution cycle
486 public void setStateFinalizerLogicMap(final Map<String, AxStateFinalizerLogic> stateFinalizerLogicMap) {
487 Assertions.argumentNotNull(stateFinalizerLogicMap, "stateFinalizerLogic may not be null");
488 this.stateFinalizerLogicMap = stateFinalizerLogicMap;
492 * Gets the default task that will execute in a state if Task Selection Logic is not specified.
494 * @return the default task that will execute in a state if Task Selection Logic is not specified
496 public AxArtifactKey getDefaultTask() {
501 * Sets the default task that will execute in a state if Task Selection Logic is not specified.
503 * @param defaultTask the default task that will execute in a state if Task Selection Logic is not specified
505 public void setDefaultTask(final AxArtifactKey defaultTask) {
506 Assertions.argumentNotNull(defaultTask, "defaultTask may not be null");
507 this.defaultTask = defaultTask;
511 * Gets the task reference map that defines the tasks for the state and how the task outputs are handled.
513 * @return the task reference map that defines the tasks for the state and how the task outputs are handled
515 public Map<AxArtifactKey, AxStateTaskReference> getTaskReferences() {
516 return taskReferenceMap;
520 * Sets the task reference map that defines the tasks for the state and how the task outputs are handled.
522 * @param taskReferences the task reference map that defines the tasks for the state and how the task outputs are
525 public void setTaskReferences(final Map<AxArtifactKey, AxStateTaskReference> taskReferences) {
526 Assertions.argumentNotNull(taskReferences, "taskReferenceMap may not be null");
527 this.taskReferenceMap = taskReferences;
534 public AxValidationResult validate(final AxValidationResult resultIn) {
535 AxValidationResult result = resultIn;
537 if (key.equals(AxReferenceKey.getNullKey())) {
538 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
539 "key is a null key"));
542 result = key.validate(result);
544 if (trigger.equals(AxArtifactKey.getNullKey())) {
545 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
546 "trigger is a null key: " + trigger));
548 result = trigger.validate(result);
550 if (stateOutputs.size() == 0) {
551 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
552 "stateOutputs may not be empty"));
554 validateStateOutputs(result);
557 validateContextAlbumReferences(result);
558 result = validateTaskSelectionLogic(result);
559 validateStateFinalizerLogics(result);
561 if (defaultTask.equals(AxArtifactKey.getNullKey())) {
562 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
563 "default task has a null key: " + defaultTask));
565 result = defaultTask.validate(result);
567 if (taskReferenceMap.size() == 0) {
568 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
569 "taskReferenceMap may not be empty"));
571 validateStateTaskReferences(result);
578 * Validate the state outputs of the state.
580 * @param result the validation result to append to
582 private void validateStateOutputs(AxValidationResult result) {
583 final Set<String> nextStateNameSet = new TreeSet<>();
584 for (final Entry<String, AxStateOutput> stateOutputEntry : stateOutputs.entrySet()) {
585 if (stateOutputEntry.getValue() == null) {
586 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
587 "null state output value found on state output " + stateOutputEntry.getKey()));
589 if (!stateOutputEntry.getValue().getKey().getParentReferenceKey().equals(key)) {
590 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
591 "parent key on state output " + stateOutputEntry.getKey()
592 + DOES_NOT_EQUAL_STATE_KEY));
595 if (stateOutputEntry.getValue().getNextState().getLocalName().equals(key.getLocalName())) {
596 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
597 "state output next state "
598 + stateOutputEntry.getValue().getNextState().getLocalName()
599 + " may not be this state"));
603 if (nextStateNameSet.contains(stateOutputEntry.getValue().getNextState().getLocalName())) {
604 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
605 "duplicate state output next state name "
606 + stateOutputEntry.getValue().getNextState().getLocalName()
609 nextStateNameSet.add(stateOutputEntry.getValue().getNextState().getLocalName());
611 result = stateOutputEntry.getValue().validate(result);
617 * Validate the context album references of the state.
619 * @param result the validation result to append to
621 private void validateContextAlbumReferences(AxValidationResult result) {
622 for (final AxArtifactKey contextAlbumReference : contextAlbumReferenceSet) {
623 if (contextAlbumReference.equals(AxArtifactKey.getNullKey())) {
624 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
625 "key on context album reference entry " + contextAlbumReference.getKey()
626 + " may not be the null key"));
629 result = contextAlbumReference.validate(result);
634 * Validate the task selection logic of the state.
636 * @param result the validation result to append to
637 * @return the result of the validation
639 private AxValidationResult validateTaskSelectionLogic(AxValidationResult result) {
640 if (!taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey())) {
641 if (!taskSelectionLogic.getKey().getParentReferenceKey().equals(key)) {
642 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
643 "taskSelectionLogic key " + taskSelectionLogic.getKey().getId()
644 + DOES_NOT_EQUAL_STATE_KEY));
646 result = taskSelectionLogic.validate(result);
653 * Validate all the state finalizer logic of the state.
655 * @param result the validation result to append to
657 private void validateStateFinalizerLogics(AxValidationResult result) {
658 for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
659 if (stateFinalizerLogicEntry.getValue() == null) {
660 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
661 "null state finalizer logic value found on state finalizer entry "
662 + stateFinalizerLogicEntry.getKey()));
664 if (!stateFinalizerLogicEntry.getValue().getKey().getParentReferenceKey().equals(key)) {
665 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
666 "stateFinalizerLogic parent key "
667 + stateFinalizerLogicEntry.getValue().getKey().getId()
668 + DOES_NOT_EQUAL_STATE_KEY));
671 result = stateFinalizerLogicEntry.getValue().validate(result);
677 * Validate the tasks used the state.
679 * @param result the validation result to append to
681 private void validateStateTaskReferences(AxValidationResult result) {
682 final Set<String> usedStateOutputNameSet = new TreeSet<>();
683 final Set<String> usedStateFinalizerLogicNameSet = new TreeSet<>();
685 for (final Entry<AxArtifactKey, AxStateTaskReference> taskRefEntry : taskReferenceMap.entrySet()) {
686 if (taskRefEntry.getKey().equals(AxArtifactKey.getNullKey())) {
687 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
688 "task has a null key: " + taskRefEntry.getKey()));
690 result = taskRefEntry.getKey().validate(result);
692 if (taskRefEntry.getValue() == null) {
693 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
694 "null task reference value found on task reference " + taskRefEntry.getKey()));
696 result = validateStateTaskReference(taskRefEntry.getKey(), taskRefEntry.getValue(),
697 usedStateOutputNameSet, usedStateFinalizerLogicNameSet, result);
701 final Set<String> unUsedStateOutputNameSet = new TreeSet<>(stateOutputs.keySet());
702 unUsedStateOutputNameSet.removeAll(usedStateOutputNameSet);
703 for (final String unUsedStateOutputName : unUsedStateOutputNameSet) {
704 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
705 "state output " + unUsedStateOutputName + " is not used directly by any task"));
708 final Set<String> usnUedStateFinalizerLogicNameSet = new TreeSet<>(stateFinalizerLogicMap.keySet());
709 usnUedStateFinalizerLogicNameSet.removeAll(usedStateFinalizerLogicNameSet);
710 for (final String unusedStateFinalizerLogicName : usnUedStateFinalizerLogicNameSet) {
711 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
712 "state finalizer logic " + unusedStateFinalizerLogicName + " is not used by any task"));
715 if (!taskReferenceMap.containsKey(defaultTask)) {
716 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
717 "defaultTask " + defaultTask + " not found in taskReferenceMap"));
722 * Validate the references of a task used in a state.
724 * @param taskKey The key of the task
725 * @param taskReference the task reference of the task
726 * @param stateOutputNameSet State outputs that have been used so far, will be appended for this task reference
727 * @param stateFinalizerLogicNameSet State finalizers that have been used so far, may be appended if this task
728 * reference uses a finalzier
729 * @param result the validation result to append to
730 * @return the result of the validation
732 private AxValidationResult validateStateTaskReference(final AxArtifactKey taskKey,
733 final AxStateTaskReference taskReference, Set<String> stateOutputNameSet,
734 Set<String> stateFinalizerLogicNameSet, AxValidationResult result) {
735 if (!taskReference.getKey().getParentReferenceKey().equals(key)) {
736 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
737 "stateTaskReference parent key " + taskReference.getKey().getId()
738 + DOES_NOT_EQUAL_STATE_KEY));
741 if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.DIRECT)) {
742 if (stateOutputs.containsKey(taskReference.getOutput().getLocalName())) {
743 stateOutputNameSet.add(taskReference.getOutput().getLocalName());
745 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
746 "state output for task " + taskKey + " not found in stateOutputs"));
748 } else if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.LOGIC)) {
749 if (stateFinalizerLogicMap.containsKey(taskReference.getOutput().getLocalName())) {
750 stateFinalizerLogicNameSet.add(taskReference.getOutput().getLocalName());
752 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
753 "state finalizer logic for task " + taskKey + " not found in stateFinalizerLogicMap"));
756 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
757 "stateTaskReference task output type " + taskReference.getStateTaskOutputType()
761 return taskReference.validate(result);
768 public void clean() {
771 for (final AxStateOutput stateOutput : stateOutputs.values()) {
774 for (final AxArtifactKey contextAlbumReference : contextAlbumReferenceSet) {
775 contextAlbumReference.clean();
777 taskSelectionLogic.clean();
778 for (final AxStateFinalizerLogic stateFinalizerLogic : stateFinalizerLogicMap.values()) {
779 stateFinalizerLogic.clean();
782 for (final AxStateTaskReference taskReference : taskReferenceMap.values()) {
783 taskReference.clean();
791 public String toString() {
792 final StringBuilder builder = new StringBuilder();
793 builder.append(this.getClass().getSimpleName());
794 builder.append(":(");
795 builder.append("stateKey=");
797 builder.append(",trigger=");
798 builder.append(trigger);
799 builder.append(",stateOutputs=");
800 builder.append(stateOutputs);
801 builder.append(",contextAlbumReferenceSet=");
802 builder.append(contextAlbumReferenceSet);
803 builder.append(",taskSelectionLogic=");
804 builder.append(taskSelectionLogic);
805 builder.append(",stateFinalizerLogicSet=");
806 builder.append(stateFinalizerLogicMap);
807 builder.append(",defaultTask=");
808 builder.append(defaultTask);
809 builder.append(",taskReferenceMap=");
810 builder.append(taskReferenceMap);
812 return builder.toString();
819 public AxConcept copyTo(final AxConcept targetObject) {
820 Assertions.argumentNotNull(targetObject, "target may not be null");
822 final Object copyObject = targetObject;
823 Assertions.instanceOf(copyObject, AxState.class);
825 final AxState copy = ((AxState) copyObject);
826 copy.setKey(new AxReferenceKey(key));
827 copy.setTrigger(new AxArtifactKey(trigger));
829 final Map<String, AxStateOutput> newStateOutputs = new TreeMap<>();
830 for (final Entry<String, AxStateOutput> stateOutputEntry : stateOutputs.entrySet()) {
831 newStateOutputs.put(stateOutputEntry.getKey(), new AxStateOutput(stateOutputEntry.getValue()));
833 copy.setStateOutputs(newStateOutputs);
835 final Set<AxArtifactKey> newContextUsage = new TreeSet<>();
836 for (final AxArtifactKey contextAlbumReferenceItem : contextAlbumReferenceSet) {
837 newContextUsage.add(new AxArtifactKey(contextAlbumReferenceItem));
839 copy.setContextAlbumReferences(newContextUsage);
841 copy.setTaskSelectionLogic(new AxTaskSelectionLogic(taskSelectionLogic));
843 final Map<String, AxStateFinalizerLogic> newStateFinalizerLogicMap = new TreeMap<>();
844 for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
845 newStateFinalizerLogicMap.put(stateFinalizerLogicEntry.getKey(),
846 new AxStateFinalizerLogic(stateFinalizerLogicEntry.getValue()));
848 copy.setStateFinalizerLogicMap(newStateFinalizerLogicMap);
850 copy.setDefaultTask(new AxArtifactKey(defaultTask));
852 final Map<AxArtifactKey, AxStateTaskReference> newTaskReferenceMap = new TreeMap<>();
853 for (final Entry<AxArtifactKey, AxStateTaskReference> taskReferenceEntry : taskReferenceMap.entrySet()) {
854 newTaskReferenceMap.put(new AxArtifactKey(taskReferenceEntry.getKey()),
855 new AxStateTaskReference(taskReferenceEntry.getValue()));
857 copy.setTaskReferences(newTaskReferenceMap);
866 public int hashCode() {
867 final int prime = 31;
869 result = prime * result + key.hashCode();
870 result = prime * result + trigger.hashCode();
871 result = prime * result + stateOutputs.hashCode();
872 result = prime * result + contextAlbumReferenceSet.hashCode();
873 result = prime * result + taskSelectionLogic.hashCode();
874 result = prime * result + stateFinalizerLogicMap.hashCode();
875 result = prime * result + defaultTask.hashCode();
876 result = prime * result + taskReferenceMap.hashCode();
884 public boolean equals(final Object obj) {
892 if (getClass() != obj.getClass()) {
896 final AxState other = (AxState) obj;
897 if (!key.equals(other.key)) {
900 if (!trigger.equals(other.trigger)) {
903 if (!stateOutputs.equals(other.stateOutputs)) {
906 if (!contextAlbumReferenceSet.equals(other.contextAlbumReferenceSet)) {
909 if (!taskSelectionLogic.equals(other.taskSelectionLogic)) {
912 if (!stateFinalizerLogicMap.equals(other.stateFinalizerLogicMap)) {
915 if (!defaultTask.equals(other.defaultTask)) {
918 return taskReferenceMap.equals(other.taskReferenceMap);
925 public int compareTo(final AxConcept otherObj) {
926 if (otherObj == null) {
929 if (this == otherObj) {
932 if (getClass() != otherObj.getClass()) {
933 return this.hashCode() - otherObj.hashCode();
936 return compareObjectFields((AxState) otherObj);
940 * Compare the object fields on this state to another state.
942 * @param the other state to compare with
943 * @return the result of the comparison
945 private int compareObjectFields(final AxState other) {
946 if (!key.equals(other.key)) {
947 return key.compareTo(other.key);
949 if (!trigger.equals(other.trigger)) {
950 return trigger.compareTo(other.trigger);
952 if (!stateOutputs.equals(other.stateOutputs)) {
953 return stateOutputs.hashCode() - other.stateOutputs.hashCode();
955 if (!contextAlbumReferenceSet.equals(other.contextAlbumReferenceSet)) {
956 return (contextAlbumReferenceSet.hashCode() - other.contextAlbumReferenceSet.hashCode());
958 if (!taskSelectionLogic.equals(other.taskSelectionLogic)) {
959 return taskSelectionLogic.compareTo(other.taskSelectionLogic);
961 if (!stateFinalizerLogicMap.equals(other.stateFinalizerLogicMap)) {
962 return stateFinalizerLogicMap.hashCode() - other.stateFinalizerLogicMap.hashCode();
964 if (!defaultTask.equals(other.defaultTask)) {
965 return defaultTask.compareTo(other.defaultTask);
967 if (!taskReferenceMap.equals(other.taskReferenceMap)) {
968 return (taskReferenceMap.hashCode() - other.taskReferenceMap.hashCode());