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