e54b880783123967b9ec9ff946212510bd8660c0
[policy/apex-pdp.git] / model / basic-model / src / main / java / org / onap / policy / apex / model / basicmodel / concepts / AxModel.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
4  *  Modifications Copyright (C) 2019 Nordix Foundation.
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.apex.model.basicmodel.concepts;
23
24 import java.util.List;
25 import java.util.Set;
26 import java.util.TreeSet;
27
28 import javax.persistence.CascadeType;
29 import javax.persistence.EmbeddedId;
30 import javax.persistence.Entity;
31 import javax.persistence.Inheritance;
32 import javax.persistence.InheritanceType;
33 import javax.persistence.JoinColumn;
34 import javax.persistence.JoinColumns;
35 import javax.persistence.OneToOne;
36 import javax.persistence.Table;
37 import javax.xml.bind.annotation.XmlAccessType;
38 import javax.xml.bind.annotation.XmlAccessorType;
39 import javax.xml.bind.annotation.XmlElement;
40 import javax.xml.bind.annotation.XmlRootElement;
41 import javax.xml.bind.annotation.XmlType;
42 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
43
44 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
45 import org.onap.policy.apex.model.basicmodel.handling.KeyInfoMarshalFilter;
46 import org.onap.policy.apex.model.basicmodel.service.ModelService;
47 import org.onap.policy.common.utils.validation.Assertions;
48
49 /**
50  * This class is the base class for all models in Apex. All model classes inherit from this model so all models must
51  * have a key and have key information.
52  *
53  * <p>Validation checks that the model key is valid. It goes on to check for null keys and checks each key for
54  * uniqueness in the model. A check is carried out to ensure that an {@link AxKeyInfo} instance exists for every
55  * {@link AxArtifactKey} key. For each {@link AxReferenceKey} instance, a check is made that its parent and local name
56  * are nut null and that a {@link AxKeyInfo} entry exists for its parent. Then a check is made that each used
57  * {@link AxArtifactKey} and {@link AxReferenceKey} usage references a key that exists. Finally, a check is made to
58  * ensure that an {@link AxArtifactKey} instance exists for every {@link AxKeyInfo} instance.
59  */
60
61 @Entity
62 @Table(name = "AxModel")
63 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
64
65 @XmlRootElement(name = "apexModel", namespace = "http://www.onap.org/policy/apex-pdp")
66 @XmlAccessorType(XmlAccessType.FIELD)
67 @XmlType(name = "AxModel", namespace = "http://www.onap.org/policy/apex-pdp", propOrder =
68     { "key", "keyInformation" })
69
70 public class AxModel extends AxConcept {
71     private static final String IS_A_NULL_KEY = " is a null key";
72
73     private static final long serialVersionUID = -771659065637205430L;
74
75     @EmbeddedId
76     @XmlElement(name = "key", required = true)
77     private AxArtifactKey key;
78
79     // @formatter:off
80     @OneToOne(cascade = CascadeType.ALL)
81     @JoinColumns({ @JoinColumn(name = "keyInformationName", referencedColumnName = "name"),
82             @JoinColumn(name = "keyInformationVersion", referencedColumnName = "version") })
83     @XmlElement(name = "keyInformation", required = true)
84     @XmlJavaTypeAdapter(KeyInfoMarshalFilter.class)
85     private AxKeyInformation keyInformation;
86     // @formatter:on
87
88     /**
89      * The Default Constructor creates this concept with a NULL artifact key.
90      */
91     public AxModel() {
92         this(new AxArtifactKey());
93     }
94
95     /**
96      * Copy constructor.
97      *
98      * @param copyConcept the concept to copy from
99      */
100     public AxModel(final AxModel copyConcept) {
101         super(copyConcept);
102     }
103
104     /**
105      * Constructor to create this concept with the specified key.
106      *
107      * @param key the key of this concept
108      */
109     public AxModel(final AxArtifactKey key) {
110         this(key, new AxKeyInformation(new AxArtifactKey(key.getName() + "_KeyInfo", key.getVersion())));
111     }
112
113     /**
114      * Constructor to create this concept and set all its fields.
115      *
116      * @param key the key of this concept
117      * @param keyInformation the key information of this concept
118      */
119     public AxModel(final AxArtifactKey key, final AxKeyInformation keyInformation) {
120         super();
121         Assertions.argumentNotNull(key, "key may not be null");
122         Assertions.argumentNotNull(keyInformation, "keyInformation may not be null");
123
124         this.key = key;
125         this.keyInformation = keyInformation;
126     }
127
128     /**
129      * Registers this model with the {@link ModelService}. All models are registered with the model service so that
130      * models can be references from anywhere in the Apex system without being passed as references through deep call
131      * chains.
132      */
133     public void register() {
134         ModelService.registerModel(AxKeyInformation.class, getKeyInformation());
135     }
136
137     /**
138      * {@inheritDoc}.
139      */
140     @Override
141     public AxArtifactKey getKey() {
142         return key;
143     }
144
145     /**
146      * {@inheritDoc}.
147      */
148     @Override
149     public List<AxKey> getKeys() {
150         final List<AxKey> keyList = key.getKeys();
151
152         // We just add the key for the KeyInformation itself. We don't add the
153         // keys from key information because
154         // that is a list of key information for existing keys
155         keyList.add(keyInformation.getKey());
156
157         return keyList;
158     }
159
160     /**
161      * Sets the key of this concept.
162      *
163      * @param key the key of this concept
164      */
165     public void setKey(final AxArtifactKey key) {
166         Assertions.argumentNotNull(key, "key may not be null");
167         this.key = key;
168     }
169
170     /**
171      * Gets the key information of this concept.
172      *
173      * @return the key information of this concept
174      */
175     public AxKeyInformation getKeyInformation() {
176         return keyInformation;
177     }
178
179     /**
180      * Sets the key information of this concept.
181      *
182      * @param keyInformation the key information of this concept
183      */
184     public void setKeyInformation(final AxKeyInformation keyInformation) {
185         Assertions.argumentNotNull(keyInformation, "keyInformation may not be null");
186         this.keyInformation = keyInformation;
187     }
188
189     /**
190      * {@inheritDoc}.
191      */
192     @Override
193     public AxValidationResult validate(final AxValidationResult resultIn) {
194         AxValidationResult result = resultIn;
195
196         if (key.equals(AxArtifactKey.getNullKey())) {
197             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
198                             "key is a null key"));
199         }
200
201         result = key.validate(result);
202         result = keyInformation.validate(result);
203
204         // Key consistency check
205         final Set<AxArtifactKey> artifactKeySet = new TreeSet<>();
206         final Set<AxReferenceKey> referenceKeySet = new TreeSet<>();
207         final Set<AxKeyUse> usedKeySet = new TreeSet<>();
208
209         for (final AxKey axKey : this.getKeys()) {
210             // Check for the two type of keys we have
211             if (axKey instanceof AxArtifactKey) {
212                 result = validateArtifactKeyInModel((AxArtifactKey) axKey, artifactKeySet, result);
213             } else if (axKey instanceof AxReferenceKey) {
214                 result = validateReferenceKeyInModel((AxReferenceKey) axKey, referenceKeySet, result);
215             }
216             // It must be an AxKeyUse, nothing else is legal
217             else {
218                 usedKeySet.add((AxKeyUse) axKey);
219             }
220         }
221
222         // Check all reference keys have correct parent keys
223         for (final AxReferenceKey referenceKey : referenceKeySet) {
224             if (!artifactKeySet.contains(referenceKey.getParentArtifactKey())) {
225                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
226                                 "parent artifact key not found for reference key " + referenceKey));
227             }
228         }
229
230         result = validateKeyUses(usedKeySet, artifactKeySet, referenceKeySet, result);
231
232         // Check key information for unused key information
233         for (final AxArtifactKey keyInfoKey : keyInformation.getKeyInfoMap().keySet()) {
234             if (!artifactKeySet.contains(keyInfoKey)) {
235                 result.addValidationMessage(new AxValidationMessage(keyInfoKey, this.getClass(),
236                                 ValidationResult.WARNING, "key not found for key information entry"));
237             }
238         }
239
240         return result;
241     }
242
243     /**
244      * Check for consistent usage of an artifact key in the model.
245      *
246      * @param artifactKey The artifact key to check
247      * @param artifactKeySet The set of artifact keys encountered so far, this key is appended to the set
248      * @param result The validation result to append to
249      * @return the result of the validation
250      */
251     private AxValidationResult validateArtifactKeyInModel(final AxArtifactKey artifactKey,
252                     final Set<AxArtifactKey> artifactKeySet, final AxValidationResult result) {
253         // Null key check
254         if (artifactKey.equals(AxArtifactKey.getNullKey())) {
255             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
256                             "key " + artifactKey + IS_A_NULL_KEY));
257         }
258
259         // Null key name start check
260         if (artifactKey.getName().toUpperCase().startsWith(AxKey.NULL_KEY_NAME)) {
261             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
262                             "key " + artifactKey + " name starts with keyword " + AxKey.NULL_KEY_NAME));
263         }
264
265         // Unique key check
266         if (artifactKeySet.contains(artifactKey)) {
267             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
268                             "duplicate key " + artifactKey + " found"));
269         } else {
270             artifactKeySet.add(artifactKey);
271         }
272
273         // Key information check
274         if (!keyInformation.getKeyInfoMap().containsKey(artifactKey)) {
275             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
276                             "key information not found for key " + artifactKey));
277         }
278
279         return result;
280     }
281
282     /**
283      * Check for consistent usage of a reference key in the model.
284      *
285      * @param artifactKey The reference key to check
286      * @param referenceKeySet The set of reference keys encountered so far, this key is appended to the set
287      * @param result The validation result to append to
288      * @return the result of the validation
289      */
290     private AxValidationResult validateReferenceKeyInModel(final AxReferenceKey referenceKey,
291                     final Set<AxReferenceKey> referenceKeySet, final AxValidationResult result) {
292         // Null key check
293         if (referenceKey.equals(AxReferenceKey.getNullKey())) {
294             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
295                             "key " + referenceKey + IS_A_NULL_KEY));
296         }
297
298         // Null parent key check
299         if (referenceKey.getParentArtifactKey().equals(AxArtifactKey.getNullKey())) {
300             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
301                             "parent artifact key of key " + referenceKey + IS_A_NULL_KEY));
302         }
303
304         // Null local name check
305         if (referenceKey.getLocalName().equals(AxKey.NULL_KEY_NAME)) {
306             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
307                             "key " + referenceKey + " has a null local name"));
308         }
309
310         // Null key name start check
311         if (referenceKey.getParentArtifactKey().getName().toUpperCase().startsWith(AxKey.NULL_KEY_NAME)) {
312             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
313                             "key " + referenceKey + " parent name starts with keyword " + AxKey.NULL_KEY_NAME));
314         }
315
316         // Unique key check
317         if (referenceKeySet.contains(referenceKey)) {
318             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
319                             "duplicate key " + referenceKey + " found"));
320         } else {
321             referenceKeySet.add(referenceKey);
322         }
323
324         // Key information check
325         if (!keyInformation.getKeyInfoMap().containsKey(referenceKey.getParentArtifactKey())) {
326             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
327                             "key information not found for parent key of key " + referenceKey));
328         }
329
330         return result;
331     }
332
333     /**
334      * Check for consistent usage of cross-key references in the model.
335      *
336      * @param usedKeySet The set of all keys used in the model
337      * @param artifactKeySet The set of artifact keys encountered so far, this key is appended to the set
338      * @param referenceKeySet The set of reference keys encountered so far, this key is appended to the set
339      * @param result The validation result to append to
340      * @return the result of the validation
341      */
342     private AxValidationResult validateKeyUses(final Set<AxKeyUse> usedKeySet, final Set<AxArtifactKey> artifactKeySet,
343                     final Set<AxReferenceKey> referenceKeySet, final AxValidationResult result) {
344         // Check all key uses
345         for (final AxKeyUse usedKey : usedKeySet) {
346             if (usedKey.getKey() instanceof AxArtifactKey) {
347                 // AxArtifact key usage, check the key exists
348                 if (!artifactKeySet.contains(usedKey.getKey())) {
349                     result.addValidationMessage(new AxValidationMessage(usedKey.getKey(), this.getClass(),
350                                     ValidationResult.INVALID, "an artifact key used in the model is not defined"));
351                 }
352             } else {
353                 // AxReference key usage, check the key exists
354                 if (!referenceKeySet.contains(usedKey.getKey())) {
355                     result.addValidationMessage(new AxValidationMessage(usedKey.getKey(), this.getClass(),
356                                     ValidationResult.INVALID, "a reference key used in the model is not defined"));
357                 }
358             }
359         }
360
361         return result;
362     }
363
364     /**
365      * {@inheritDoc}.
366      */
367     @Override
368     public void clean() {
369         key.clean();
370         keyInformation.clean();
371     }
372
373     /**
374      * {@inheritDoc}.
375      */
376     @Override
377     public String toString() {
378         final StringBuilder builder = new StringBuilder();
379         builder.append(this.getClass().getSimpleName());
380         builder.append(":(");
381         builder.append("key=");
382         builder.append(key);
383         builder.append(",keyInformation=");
384         builder.append(keyInformation);
385         builder.append(")");
386         return builder.toString();
387     }
388
389     /**
390      * {@inheritDoc}.
391      */
392     @Override
393     public AxConcept copyTo(final AxConcept target) {
394         Assertions.argumentNotNull(target, "target may not be null");
395
396         final Object copyObject = target;
397         Assertions.instanceOf(copyObject, AxModel.class);
398
399         final AxModel copy = ((AxModel) copyObject);
400         copy.setKey(new AxArtifactKey(key));
401         copy.setKeyInformation(new AxKeyInformation(keyInformation));
402
403         return copy;
404     }
405
406     /**
407      * {@inheritDoc}.
408      */
409     @Override
410     public int hashCode() {
411         final int prime = 31;
412         int result = 1;
413         result = prime * result + key.hashCode();
414         result = prime * result + keyInformation.hashCode();
415         return result;
416     }
417
418     /**
419      * {@inheritDoc}.
420      */
421     @Override
422     public boolean equals(final Object obj) {
423         if (obj == null) {
424             return false;
425         }
426         if (this == obj) {
427             return true;
428         }
429         if (getClass() != obj.getClass()) {
430             return false;
431         }
432
433         final AxModel other = (AxModel) obj;
434         if (!key.equals(other.key)) {
435             return false;
436         }
437         return keyInformation.equals(other.keyInformation);
438     }
439
440     /**
441      * {@inheritDoc}.
442      */
443     @Override
444     public int compareTo(final AxConcept otherObj) {
445         if (otherObj == null) {
446             return -1;
447         }
448         if (this == otherObj) {
449             return 0;
450         }
451         if (getClass() != otherObj.getClass()) {
452             return this.hashCode() - otherObj.hashCode();
453         }
454
455         final AxModel other = (AxModel) otherObj;
456         if (!key.equals(other.key)) {
457             return key.compareTo(other.key);
458         }
459         return keyInformation.compareTo(other.keyInformation);
460     }
461 }