83ae71cca659b28c0beae32756522e280b75a649
[policy/models.git] / models-base / src / main / java / org / onap / policy / models / base / PfModel.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2019 Nordix Foundation.
4  *  Modifications Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.models.base;
23
24 import java.util.List;
25 import java.util.Set;
26 import java.util.TreeSet;
27
28 import javax.persistence.EmbeddedId;
29 import javax.persistence.Entity;
30 import javax.persistence.Inheritance;
31 import javax.persistence.InheritanceType;
32 import javax.persistence.Table;
33
34 import lombok.Data;
35 import lombok.EqualsAndHashCode;
36 import lombok.NonNull;
37
38 import org.onap.policy.common.utils.validation.Assertions;
39 import org.onap.policy.models.base.PfValidationResult.ValidationResult;
40
41 /**
42  * This class is the base class for all models in the Policy Framework. All model classes inherit
43  * from this model so all models must have a key and have key information.
44  *
45  * <p>Validation checks that the model key is valid. It goes on to check for null keys and checks
46  * each key for uniqueness in the model. A check is carried out to ensure that an {@link PfKeyInfo}
47  * instance exists for every {@link PfConceptKey} key. For each {@link PfReferenceKey} instance, a
48  * check is made that its parent and local name are nut null and that a {@link PfKeyInfo} entry
49  * exists for its parent. Then a check is made that each used {@link PfConceptKey} and
50  * {@link PfReferenceKey} usage references a key that exists. Finally, a check is made to ensure
51  * that an {@link PfConceptKey} instance exists for every {@link PfKeyInfo} instance.
52  *
53  * @param <C> the type of concept on which the interface is applied.
54  */
55
56 @Entity
57 @Table(name = "PfModel")
58 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
59 @Data
60 @EqualsAndHashCode(callSuper = false)
61 public abstract class PfModel extends PfConcept {
62     private static final String IS_A_NULL_KEY = " is a null key";
63
64     private static final long serialVersionUID = -771659065637205430L;
65
66     @EmbeddedId
67     private PfConceptKey key;
68
69     /**
70      * The Default Constructor creates this concept with a NULL artifact key.
71      */
72     public PfModel() {
73         this(new PfConceptKey());
74     }
75
76     /**
77      * Constructor to create this concept with the specified key.
78      *
79      * @param key the key of this concept
80      */
81     public PfModel(@NonNull final PfConceptKey key) {
82         super();
83         Assertions.argumentNotNull(key, "key may not be null");
84
85         this.key = key;
86     }
87
88     /**
89      * Copy constructor.
90      *
91      * @param copyConcept the concept to copy from
92      */
93     public PfModel(@NonNull final PfModel copyConcept) {
94         super(copyConcept);
95         this.key = new PfConceptKey(copyConcept.key);
96     }
97
98     /**
99      * Registers this model with the {@link PfModelService}. All models are registered with the
100      * model service so that models can be references from anywhere in the Policy Framework system
101      * without being passed as references through deep call chains.
102      */
103     public abstract void register();
104
105     @Override
106     public List<PfKey> getKeys() {
107         return key.getKeys();
108     }
109
110     @Override
111     public void clean() {
112         key.clean();
113     }
114
115     @Override
116     public PfValidationResult validate(@NonNull final PfValidationResult resultIn) {
117         PfValidationResult result = resultIn;
118
119         if (key.isNullKey()) {
120             result.addValidationMessage(
121                     new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
122         }
123
124         result = key.validate(result);
125
126         // Key consistency check
127         final Set<PfConceptKey> artifactKeySet = new TreeSet<>();
128         final Set<PfReferenceKey> referenceKeySet = new TreeSet<>();
129         final Set<PfKeyUse> usedKeySet = new TreeSet<>();
130
131         for (final PfKey pfKey : this.getKeys()) {
132             // Check for the two type of keys we have
133             if (pfKey instanceof PfConceptKey) {
134                 result = validateArtifactKeyInModel((PfConceptKey) pfKey, artifactKeySet, result);
135             } else if (pfKey instanceof PfReferenceKey) {
136                 result = validateReferenceKeyInModel((PfReferenceKey) pfKey, referenceKeySet, result);
137             }
138             // It must be a PfKeyUse, nothing else is legal
139             else {
140                 usedKeySet.add((PfKeyUse) pfKey);
141             }
142         }
143
144         // Check all reference keys have correct parent keys
145         for (final PfReferenceKey referenceKey : referenceKeySet) {
146             if (!artifactKeySet.contains(referenceKey.getParentConceptKey())) {
147                 result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
148                         "parent artifact key not found for reference key " + referenceKey));
149             }
150         }
151
152         result = validateKeyUses(usedKeySet, artifactKeySet, referenceKeySet, result);
153
154         return result;
155     }
156
157     /**
158      * Check for consistent usage of an artifact key in the model.
159      *
160      * @param artifactKey The artifact key to check
161      * @param artifactKeySet The set of artifact keys encountered so far, this key is appended to
162      *        the set
163      * @param result The validation result to append to
164      * @return the result of the validation
165      */
166     private PfValidationResult validateArtifactKeyInModel(final PfConceptKey artifactKey,
167             final Set<PfConceptKey> artifactKeySet, final PfValidationResult result) {
168         // Null key check
169         if (artifactKey.isNullKey()) {
170             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
171                     "key " + artifactKey + IS_A_NULL_KEY));
172         }
173
174         // Null key name start check
175         if (artifactKey.getName().toUpperCase().startsWith(PfKey.NULL_KEY_NAME)) {
176             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
177                     "key " + artifactKey + " name starts with keyword " + PfKey.NULL_KEY_NAME));
178         }
179
180         // Unique key check
181         if (artifactKeySet.contains(artifactKey)) {
182             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
183                     "duplicate key " + artifactKey + " found"));
184         } else {
185             artifactKeySet.add(artifactKey);
186         }
187
188         return result;
189     }
190
191     /**
192      * Check for consistent usage of a reference key in the model.
193      *
194      * @param artifactKey The reference key to check
195      * @param referenceKeySet The set of reference keys encountered so far, this key is appended to
196      *        the set
197      * @param result The validation result to append to
198      * @return the result of the validation
199      */
200     private PfValidationResult validateReferenceKeyInModel(final PfReferenceKey referenceKey,
201             final Set<PfReferenceKey> referenceKeySet, final PfValidationResult result) {
202         // Null key check
203         if (referenceKey.isNullKey()) {
204             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
205                     "key " + referenceKey + IS_A_NULL_KEY));
206         }
207
208         // Null parent key check
209         if (referenceKey.getParentConceptKey().isNullKey()) {
210             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
211                     "parent artifact key of key " + referenceKey + IS_A_NULL_KEY));
212         }
213
214         // Null local name check
215         if (referenceKey.getLocalName().equals(PfKey.NULL_KEY_NAME)) {
216             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
217                     "key " + referenceKey + " has a null local name"));
218         }
219
220         // Null key name start check
221         if (referenceKey.getParentConceptKey().getName().toUpperCase().startsWith(PfKey.NULL_KEY_NAME)) {
222             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
223                     "key " + referenceKey + " parent name starts with keyword " + PfKey.NULL_KEY_NAME));
224         }
225
226         // Unique key check
227         if (referenceKeySet.contains(referenceKey)) {
228             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
229                     "duplicate key " + referenceKey + " found"));
230         } else {
231             referenceKeySet.add(referenceKey);
232         }
233
234         return result;
235     }
236
237     /**
238      * Check for consistent usage of cross-key references in the model.
239      *
240      * @param usedKeySet The set of all keys used in the model
241      * @param artifactKeySet The set of artifact keys encountered so far, this key is appended to
242      *        the set
243      * @param referenceKeySet The set of reference keys encountered so far, this key is appended to
244      *        the set
245      * @param result The validation result to append to
246      * @return the result of the validation
247      */
248     private PfValidationResult validateKeyUses(final Set<PfKeyUse> usedKeySet, final Set<PfConceptKey> artifactKeySet,
249             final Set<PfReferenceKey> referenceKeySet, final PfValidationResult result) {
250         // Check all key uses
251         for (final PfKeyUse usedKey : usedKeySet) {
252             if (usedKey.getKey() instanceof PfConceptKey) {
253                 // PfConceptKey usage, check the key exists
254                 if (!artifactKeySet.contains(usedKey.getKey())) {
255                     result.addValidationMessage(new PfValidationMessage(usedKey.getKey(), this.getClass(),
256                             ValidationResult.INVALID, "an artifact key used in the model is not defined"));
257                 }
258             } else {
259                 // PfReferenceKey usage, check the key exists
260                 if (!referenceKeySet.contains(usedKey.getKey())) {
261                     result.addValidationMessage(new PfValidationMessage(usedKey.getKey(), this.getClass(),
262                             ValidationResult.INVALID, "a reference key used in the model is not defined"));
263                 }
264             }
265         }
266
267         return result;
268     }
269
270     @Override
271     public int compareTo(final PfConcept otherObj) {
272         if (otherObj == null) {
273             return -1;
274         }
275         if (this == otherObj) {
276             return 0;
277         }
278         if (getClass() != otherObj.getClass()) {
279             return getClass().getName().compareTo(otherObj.getClass().getName());
280         }
281
282         final PfModel other = (PfModel) otherObj;
283
284         return key.compareTo(other.key);
285     }
286 }