Merge "Add classes for PDP "simple" REST APIs"
[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     private PfConceptKey key;
67
68     /**
69      * The Default Constructor creates this concept with a NULL artifact key.
70      */
71     public PfModel() {
72         this(new PfConceptKey());
73     }
74
75     /**
76      * Constructor to create this concept with the specified key.
77      *
78      * @param key the key of this concept
79      */
80     public PfModel(@NonNull final PfConceptKey key) {
81         super();
82         Assertions.argumentNotNull(key, "key may not be null");
83
84         this.key = key;
85     }
86
87     /**
88      * Copy constructor.
89      *
90      * @param copyConcept the concept to copy from
91      */
92     public PfModel(@NonNull final PfModel copyConcept) {
93         super(copyConcept);
94     }
95
96     /**
97      * Registers this model with the {@link PfModelService}. All models are registered with the
98      * model service so that models can be references from anywhere in the Policy Framework system
99      * without being passed as references through deep call chains.
100      */
101     public abstract void register();
102
103     @Override
104     public List<PfKey> getKeys() {
105         return key.getKeys();
106     }
107
108     @Override
109     public void clean() {
110         key.clean();
111     }
112
113     @Override
114     public PfValidationResult validate(@NonNull final PfValidationResult resultIn) {
115         PfValidationResult result = resultIn;
116
117         if (key.isNullKey()) {
118             result.addValidationMessage(
119                     new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
120         }
121
122         result = key.validate(result);
123
124         // Key consistency check
125         final Set<PfConceptKey> artifactKeySet = new TreeSet<>();
126         final Set<PfReferenceKey> referenceKeySet = new TreeSet<>();
127         final Set<PfKeyUse> usedKeySet = new TreeSet<>();
128
129         for (final PfKey pfKey : this.getKeys()) {
130             // Check for the two type of keys we have
131             if (pfKey instanceof PfConceptKey) {
132                 result = validateArtifactKeyInModel((PfConceptKey) pfKey, artifactKeySet, result);
133             } else if (pfKey instanceof PfReferenceKey) {
134                 result = validateReferenceKeyInModel((PfReferenceKey) pfKey, referenceKeySet, result);
135             }
136             // It must be a PfKeyUse, nothing else is legal
137             else {
138                 usedKeySet.add((PfKeyUse) pfKey);
139             }
140         }
141
142         // Check all reference keys have correct parent keys
143         for (final PfReferenceKey referenceKey : referenceKeySet) {
144             if (!artifactKeySet.contains(referenceKey.getParentConceptKey())) {
145                 result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
146                         "parent artifact key not found for reference key " + referenceKey));
147             }
148         }
149
150         result = validateKeyUses(usedKeySet, artifactKeySet, referenceKeySet, result);
151
152         return result;
153     }
154
155     /**
156      * Check for consistent usage of an artifact key in the model.
157      *
158      * @param artifactKey The artifact key to check
159      * @param artifactKeySet The set of artifact keys encountered so far, this key is appended to
160      *        the set
161      * @param result The validation result to append to
162      * @return the result of the validation
163      */
164     private PfValidationResult validateArtifactKeyInModel(final PfConceptKey artifactKey,
165             final Set<PfConceptKey> artifactKeySet, final PfValidationResult result) {
166         // Null key check
167         if (artifactKey.isNullKey()) {
168             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
169                     "key " + artifactKey + IS_A_NULL_KEY));
170         }
171
172         // Null key name start check
173         if (artifactKey.getName().toUpperCase().startsWith(PfKey.NULL_KEY_NAME)) {
174             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
175                     "key " + artifactKey + " name starts with keyword " + PfKey.NULL_KEY_NAME));
176         }
177
178         // Unique key check
179         if (artifactKeySet.contains(artifactKey)) {
180             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
181                     "duplicate key " + artifactKey + " found"));
182         } else {
183             artifactKeySet.add(artifactKey);
184         }
185
186         return result;
187     }
188
189     /**
190      * Check for consistent usage of a reference key in the model.
191      *
192      * @param artifactKey The reference key to check
193      * @param referenceKeySet The set of reference keys encountered so far, this key is appended to
194      *        the set
195      * @param result The validation result to append to
196      * @return the result of the validation
197      */
198     private PfValidationResult validateReferenceKeyInModel(final PfReferenceKey referenceKey,
199             final Set<PfReferenceKey> referenceKeySet, final PfValidationResult result) {
200         // Null key check
201         if (referenceKey.isNullKey()) {
202             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
203                     "key " + referenceKey + IS_A_NULL_KEY));
204         }
205
206         // Null parent key check
207         if (referenceKey.getParentConceptKey().isNullKey()) {
208             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
209                     "parent artifact key of key " + referenceKey + IS_A_NULL_KEY));
210         }
211
212         // Null local name check
213         if (referenceKey.getLocalName().equals(PfKey.NULL_KEY_NAME)) {
214             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
215                     "key " + referenceKey + " has a null local name"));
216         }
217
218         // Null key name start check
219         if (referenceKey.getParentConceptKey().getName().toUpperCase().startsWith(PfKey.NULL_KEY_NAME)) {
220             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
221                     "key " + referenceKey + " parent name starts with keyword " + PfKey.NULL_KEY_NAME));
222         }
223
224         // Unique key check
225         if (referenceKeySet.contains(referenceKey)) {
226             result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
227                     "duplicate key " + referenceKey + " found"));
228         } else {
229             referenceKeySet.add(referenceKey);
230         }
231
232         return result;
233     }
234
235     /**
236      * Check for consistent usage of cross-key references in the model.
237      *
238      * @param usedKeySet The set of all keys used in the model
239      * @param artifactKeySet The set of artifact keys encountered so far, this key is appended to
240      *        the set
241      * @param referenceKeySet The set of reference keys encountered so far, this key is appended to
242      *        the set
243      * @param result The validation result to append to
244      * @return the result of the validation
245      */
246     private PfValidationResult validateKeyUses(final Set<PfKeyUse> usedKeySet, final Set<PfConceptKey> artifactKeySet,
247             final Set<PfReferenceKey> referenceKeySet, final PfValidationResult result) {
248         // Check all key uses
249         for (final PfKeyUse usedKey : usedKeySet) {
250             if (usedKey.getKey() instanceof PfConceptKey) {
251                 // PfConceptKey usage, check the key exists
252                 if (!artifactKeySet.contains(usedKey.getKey())) {
253                     result.addValidationMessage(new PfValidationMessage(usedKey.getKey(), this.getClass(),
254                             ValidationResult.INVALID, "an artifact key used in the model is not defined"));
255                 }
256             } else {
257                 // PfReferenceKey usage, check the key exists
258                 if (!referenceKeySet.contains(usedKey.getKey())) {
259                     result.addValidationMessage(new PfValidationMessage(usedKey.getKey(), this.getClass(),
260                             ValidationResult.INVALID, "a reference key used in the model is not defined"));
261                 }
262             }
263         }
264
265         return result;
266     }
267
268     @Override
269     public int compareTo(final PfConcept otherObj) {
270         if (otherObj == null) {
271             return -1;
272         }
273         if (this == otherObj) {
274             return 0;
275         }
276         if (getClass() != otherObj.getClass()) {
277             return this.hashCode() - otherObj.hashCode();
278         }
279
280         final PfModel other = (PfModel) otherObj;
281
282         return key.compareTo(other.key);
283     }
284
285     @Override
286     public PfConcept copyTo(@NonNull final PfConcept target) {
287         Assertions.instanceOf(target, PfModel.class);
288
289         final PfModel copy = ((PfModel) target);
290         copy.setKey(new PfConceptKey(key));
291
292         return copy;
293     }
294 }