e7b79cadfb46f23f0fa8e3e5715dd43b472031dd
[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-2021 AT&T Intellectual Property. All rights reserved.
5  *  Modifications Copyright (C) 2022 Bell Canada. All rights reserved.
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.models.base;
24
25 import java.util.List;
26 import java.util.Set;
27 import java.util.TreeSet;
28 import javax.persistence.EmbeddedId;
29 import javax.persistence.MappedSuperclass;
30 import lombok.Data;
31 import lombok.EqualsAndHashCode;
32 import lombok.NonNull;
33 import org.onap.policy.common.parameters.BeanValidationResult;
34 import org.onap.policy.common.parameters.ValidationStatus;
35 import org.onap.policy.common.parameters.annotations.NotNull;
36 import org.onap.policy.common.utils.validation.Assertions;
37 import org.onap.policy.models.base.validation.annotations.VerifyKey;
38
39 /**
40  * This class is the base class for all models in the Policy Framework. All model classes inherit
41  * from this model so all models must have a key and have key information.
42  *
43  * <p>Validation checks that the model key is valid. It goes on to check for null keys and checks
44  * each key for uniqueness in the model. A check is carried out to ensure that an {@link PfKeyInfo}
45  * instance exists for every {@link PfConceptKey} key. For each {@link PfReferenceKey} instance, a
46  * check is made that its parent and local name are nut null and that a {@link PfKeyInfo} entry
47  * exists for its parent. Then a check is made that each used {@link PfConceptKey} and
48  * {@link PfReferenceKey} usage references a key that exists. Finally, a check is made to ensure
49  * that an {@link PfConceptKey} instance exists for every {@link PfKeyInfo} instance.
50  *
51  * @param <C> the type of concept on which the interface is applied.
52  */
53
54 @MappedSuperclass
55 @Data
56 @EqualsAndHashCode(callSuper = false)
57 public abstract class PfModel extends PfConcept {
58     private static final String KEYS_TOKEN = "keys";
59
60     private static final long serialVersionUID = -771659065637205430L;
61
62     @EmbeddedId
63     @VerifyKey
64     @NotNull
65     private PfConceptKey key;
66
67     /**
68      * The Default Constructor creates this concept with a NULL artifact key.
69      */
70     protected PfModel() {
71         this(new PfConceptKey());
72     }
73
74     /**
75      * Constructor to create this concept with the specified key.
76      *
77      * @param key the key of this concept
78      */
79     protected PfModel(@NonNull final PfConceptKey key) {
80         super();
81         Assertions.argumentNotNull(key, "key may not be null");
82
83         this.key = key;
84     }
85
86     /**
87      * Copy constructor.
88      *
89      * @param copyConcept the concept to copy from
90      */
91     protected PfModel(@NonNull final PfModel copyConcept) {
92         super(copyConcept);
93         this.key = new PfConceptKey(copyConcept.key);
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 BeanValidationResult validate(@NonNull String fieldName) {
115         BeanValidationResult result = new PfValidator().validateTop(fieldName, this);
116
117         // Key consistency check
118         final Set<PfConceptKey> artifactKeySet = new TreeSet<>();
119         final Set<PfReferenceKey> referenceKeySet = new TreeSet<>();
120         final Set<PfKeyUse> usedKeySet = new TreeSet<>();
121
122         for (final PfKey pfKey : this.getKeys()) {
123             // Check for the two type of keys we have
124             if (pfKey instanceof PfConceptKey) {
125                 validateArtifactKeyInModel((PfConceptKey) pfKey, artifactKeySet, result);
126             } else if (pfKey instanceof PfReferenceKey) {
127                 validateReferenceKeyInModel((PfReferenceKey) pfKey, referenceKeySet, result);
128             } else {
129                 // It must be a PfKeyUse, nothing else is legal
130                 usedKeySet.add((PfKeyUse) pfKey);
131             }
132         }
133
134         // Check all reference keys have correct parent keys
135         for (final PfReferenceKey referenceKey : referenceKeySet) {
136             if (!artifactKeySet.contains(referenceKey.getParentConceptKey())) {
137                 addResult(result, "reference key", referenceKey, "parent artifact key not found");
138             }
139         }
140
141         validateKeyUses(usedKeySet, artifactKeySet, referenceKeySet, result);
142
143         return result;
144     }
145
146     /**
147      * Check for consistent usage of an artifact key in the model.
148      *
149      * @param artifactKey The artifact key to check
150      * @param artifactKeySet The set of artifact keys encountered so far, this key is appended to
151      *        the set
152      * @param result where to add the results
153      */
154     private void validateArtifactKeyInModel(final PfConceptKey artifactKey,
155             final Set<PfConceptKey> artifactKeySet, final BeanValidationResult result) {
156
157         validateKeyNotNull(result, KEYS_TOKEN, artifactKey);
158
159         var result2 = new BeanValidationResult(KEYS_TOKEN, artifactKey);
160
161         // Null key name start check
162         if (artifactKey.getName().toUpperCase().startsWith(PfKey.NULL_KEY_NAME)) {
163             addResult(result2, "name of " + artifactKey.getId(), artifactKey.getName(),
164                             "starts with keyword " + PfKey.NULL_KEY_NAME);
165         }
166
167         // Unique key check
168         if (artifactKeySet.contains(artifactKey)) {
169             addResult(result, KEYS_TOKEN, artifactKey, "duplicate key");
170         } else {
171             artifactKeySet.add(artifactKey);
172         }
173     }
174
175     /**
176      * Check for consistent usage of a reference key in the model.
177      *
178      * @param referenceKey The reference key to check
179      * @param referenceKeySet The set of reference keys encountered so far, this key is appended to
180      *        the set
181      * @param result where to add the results
182      */
183     private void validateReferenceKeyInModel(final PfReferenceKey referenceKey,
184             final Set<PfReferenceKey> referenceKeySet, final BeanValidationResult result) {
185         // Null key check
186         if (referenceKey.isNullKey()) {
187             addResult(result, KEYS_TOKEN, referenceKey, IS_A_NULL_KEY);
188         }
189
190         var result2 = new BeanValidationResult(KEYS_TOKEN, referenceKey);
191
192         // Null parent key check
193         if (referenceKey.getParentConceptKey().isNullKey()) {
194             addResult(result2, "parent key of " + referenceKey.getId(), referenceKey.getParentConceptKey().getId(),
195                             IS_A_NULL_KEY);
196         }
197
198         // Null local name check
199         if (referenceKey.getLocalName().equals(PfKey.NULL_KEY_NAME)) {
200             addResult(result2, "local name of " + referenceKey.getId(), referenceKey.getLocalName(), IS_NULL);
201         }
202
203         // Null key name start check
204         if (referenceKey.getParentConceptKey().getName().toUpperCase().startsWith(PfKey.NULL_KEY_NAME)) {
205             addResult(result2, "parent name of " + referenceKey.getId(), referenceKey.getParentConceptKey().getName(),
206                             "starts with keyword " + PfKey.NULL_KEY_NAME);
207         }
208
209         // Unique key check
210         if (referenceKeySet.contains(referenceKey)) {
211             addResult(result, KEYS_TOKEN, referenceKey, "duplicate key");
212         } else {
213             referenceKeySet.add(referenceKey);
214         }
215     }
216
217     /**
218      * Check for consistent usage of cross-key references in the model.
219      *
220      * @param usedKeySet The set of all keys used in the model
221      * @param artifactKeySet The set of artifact keys encountered so far, this key is appended to
222      *        the set
223      * @param referenceKeySet The set of reference keys encountered so far, this key is appended to
224      *        the set
225      * @param result where to add the results
226      */
227     private void validateKeyUses(final Set<PfKeyUse> usedKeySet, final Set<PfConceptKey> artifactKeySet,
228             final Set<PfReferenceKey> referenceKeySet, final BeanValidationResult result) {
229         // Check all key uses
230         for (final PfKeyUse usedKey : usedKeySet) {
231             if (usedKey.getKey() instanceof PfConceptKey) {
232                 // PfConceptKey usage, check the key exists
233                 if (!artifactKeySet.contains(usedKey.getKey())) {
234                     result.addResult("artifact key", usedKey.getId(), ValidationStatus.INVALID, NOT_DEFINED);
235                 }
236             } else {
237                 // PfReferenceKey usage, check the key exists
238                 if (!referenceKeySet.contains(usedKey.getKey())) {
239                     result.addResult("reference key", usedKey.getId(), ValidationStatus.INVALID, NOT_DEFINED);
240                 }
241             }
242         }
243     }
244
245     @Override
246     public int compareTo(final PfConcept otherObj) {
247         if (otherObj == null) {
248             return -1;
249         }
250         if (this == otherObj) {
251             return 0;
252         }
253         if (getClass() != otherObj.getClass()) {
254             return getClass().getName().compareTo(otherObj.getClass().getName());
255         }
256
257         final PfModel other = (PfModel) otherObj;
258
259         return key.compareTo(other.key);
260     }
261 }