949cb96dc8bf27c1811e05faf9644dba51a75790
[policy/models.git] / models-base / src / main / java / org / onap / policy / models / base / PfConceptContainer.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2019-2020 Nordix Foundation.
4  *  Modifications Copyright (C) 2019-2020 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.lang.reflect.ParameterizedType;
25 import java.util.ArrayList;
26 import java.util.LinkedHashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.NavigableMap;
31 import java.util.Set;
32 import java.util.TreeMap;
33
34 import javax.persistence.CascadeType;
35 import javax.persistence.EmbeddedId;
36 import javax.persistence.Entity;
37 import javax.persistence.JoinColumn;
38 import javax.persistence.JoinTable;
39 import javax.persistence.ManyToMany;
40 import javax.persistence.MappedSuperclass;
41 import javax.persistence.Table;
42 import javax.ws.rs.core.Response;
43
44 import lombok.Data;
45 import lombok.EqualsAndHashCode;
46 import lombok.NonNull;
47
48 import org.onap.policy.models.base.PfValidationResult.ValidationResult;
49
50 // @formatter:off
51 /**
52  * This class is a concept container and holds a map of concepts. The {@link PfConceptContainer} class implements the
53  * helper methods of the {@link PfConceptGetter} interface to allow {@link PfConceptContainer} instances to be retrieved
54  * by calling methods directly on this class without referencing the contained map.
55  *
56  * <p>Validation checks that a container key is not null. An error is issued if no concepts are defined in a container.
57  * Each concept entry is checked to ensure that its key and value are not null and that the key matches the key in the
58  * map value. Each concept entry is then validated individually.
59  *
60  * @param C the concept being contained
61  */
62 //@formatter:on
63 @MappedSuperclass
64 @Entity
65 @Table(name = "PfConceptContainer")
66 @Data
67 @EqualsAndHashCode(callSuper = false)
68
69 public class PfConceptContainer<C extends PfConcept, A extends PfNameVersion> extends PfConcept
70         implements PfConceptGetter<C>, PfAuthorative<List<Map<String, A>>> {
71     private static final long serialVersionUID = -324211738823208318L;
72
73     @EmbeddedId
74     private PfConceptKey key;
75
76     @ManyToMany(cascade = CascadeType.ALL)
77     // @formatter:off
78     @JoinTable(
79             joinColumns = {
80                 @JoinColumn(name = "conceptContainerMapName",    referencedColumnName = "name"),
81                 @JoinColumn(name = "concpetContainerMapVersion", referencedColumnName = "version")
82             },
83             inverseJoinColumns = {
84                 @JoinColumn(name = "conceptContainerName",    referencedColumnName = "name"),
85                 @JoinColumn(name = "conceptContainerVersion", referencedColumnName = "version")
86             }
87         )
88     // @formatter:on
89     private Map<PfConceptKey, C> conceptMap;
90
91     /**
92      * The Default Constructor creates a {@link PfConceptContainer} object with a null artifact key and creates an empty
93      * concept map.
94      */
95     public PfConceptContainer() {
96         this(new PfConceptKey());
97     }
98
99     /**
100      * The Key Constructor creates a {@link PfConceptContainer} object with the given artifact key and creates an empty
101      * concept map.
102      *
103      * @param key the concept key
104      */
105     public PfConceptContainer(@NonNull final PfConceptKey key) {
106         this(key, new TreeMap<PfConceptKey, C>());
107     }
108
109     /**
110      * This Constructor creates an concept container with all of its fields defined.
111      *
112      * @param key the concept container key
113      * @param conceptMap the concepts to be stored in the concept container
114      */
115     public PfConceptContainer(@NonNull final PfConceptKey key, @NonNull final Map<PfConceptKey, C> conceptMap) {
116         super();
117
118         this.key = key;
119         this.conceptMap = new TreeMap<>(conceptMap);
120     }
121
122     /**
123      * Copy constructor.
124      *
125      * @param copyConcept the concept to copy from
126      */
127     public PfConceptContainer(@NonNull final PfConceptContainer<C, A> copyConcept) {
128         super(copyConcept);
129         this.key = new PfConceptKey(copyConcept.key);
130
131         this.conceptMap = new TreeMap<>();
132         for (final Entry<PfConceptKey, C> conceptMapEntry : copyConcept.conceptMap.entrySet()) {
133             PfConceptKey newK = new PfConceptKey(conceptMapEntry.getKey());
134             C newC = PfUtils.makeCopy(conceptMapEntry.getValue());
135             this.conceptMap.put(newK, newC);
136         }
137     }
138
139     @Override
140     public List<PfKey> getKeys() {
141         final List<PfKey> keyList = key.getKeys();
142
143         for (final C concept : conceptMap.values()) {
144             keyList.addAll(concept.getKeys());
145         }
146
147         return keyList;
148     }
149
150     @Override
151     public List<Map<String, A>> toAuthorative() {
152         // The returned list is a list of map singletons with one map for each map
153         // entry in the concept container
154         List<Map<String, A>> toscaPolicyMapList = new ArrayList<>();
155
156         for (Entry<PfConceptKey, C> conceptEntry : getConceptMap().entrySet()) {
157             // Create a map to hold this entry
158             Map<String, A> toscaPolicyMap = new LinkedHashMap<>(1);
159
160             // Add the concept container entry to the singleton map
161             @SuppressWarnings("unchecked")
162             PfAuthorative<A> authoritiveImpl = (PfAuthorative<A>) conceptEntry.getValue();
163             toscaPolicyMap.put(conceptEntry.getKey().getName(), authoritiveImpl.toAuthorative());
164
165             // Add the map to the returned list
166             toscaPolicyMapList.add(toscaPolicyMap);
167         }
168
169         return toscaPolicyMapList;
170     }
171
172     @Override
173     public void fromAuthorative(List<Map<String, A>> authorativeList) {
174         // Clear any existing map entries
175         conceptMap.clear();
176
177         // Concepts are in lists of maps
178         for (Map<String, A> incomingConceptMap : authorativeList) {
179             // Add the map entries one by one
180             for (Entry<String, A> incomingConceptEntry : incomingConceptMap.entrySet()) {
181                 C jpaConcept = getConceptNewInstance();
182
183                 // This cast allows us to call the fromAuthorative method
184                 @SuppressWarnings("unchecked")
185                 PfAuthorative<A> authoritiveImpl = (PfAuthorative<A>) jpaConcept;
186
187                 if (incomingConceptEntry.getValue().getName() == null) {
188                     incomingConceptEntry.getValue().setName(incomingConceptEntry.getKey());
189                 }
190
191                 // Set the key name and the rest of the values on the concept
192                 authoritiveImpl.fromAuthorative(incomingConceptEntry.getValue());
193
194                 // This cast gets the key of the concept
195                 PfConceptKey conceptKey = (PfConceptKey) jpaConcept.getKey();
196
197                 // Set the concept key of the concept
198                 conceptKey.setName(incomingConceptEntry.getValue().getName());
199
200                 if (incomingConceptEntry.getValue().getVersion() != null) {
201                     conceptKey.setVersion(incomingConceptEntry.getValue().getVersion());
202                 } else {
203                     conceptKey.setVersion(PfKey.NULL_KEY_VERSION);
204                 }
205
206                 // After all that, save the map entry
207                 conceptMap.put(conceptKey, jpaConcept);
208             }
209         }
210
211         if (conceptMap.isEmpty()) {
212             throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
213                     "An incoming list of concepts must have at least one entry");
214         }
215     }
216
217     @Override
218     public void clean() {
219         key.clean();
220         for (final Entry<PfConceptKey, C> conceptEntry : conceptMap.entrySet()) {
221             conceptEntry.getKey().clean();
222             conceptEntry.getValue().clean();
223         }
224     }
225
226     @Override
227     public PfValidationResult validate(@NonNull final PfValidationResult resultIn) {
228         PfValidationResult result = resultIn;
229
230         if (key.equals(PfConceptKey.getNullKey())) {
231             result.addValidationMessage(
232                     new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
233         }
234
235         result = key.validate(result);
236
237         if (!conceptMap.isEmpty()) {
238             result = validateConceptMap(result);
239         }
240
241         return result;
242     }
243
244     /**
245      * Validate the concept map of the container.
246      *
247      * @param resultIn the incoming validation results so far
248      * @return the validation results with the results of this validation added
249      */
250     private PfValidationResult validateConceptMap(final PfValidationResult resultIn) {
251         PfValidationResult result = resultIn;
252
253         for (final Entry<PfConceptKey, C> conceptEntry : conceptMap.entrySet()) {
254             if (conceptEntry.getKey().equals(PfConceptKey.getNullKey())) {
255                 result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
256                         "key on concept entry " + conceptEntry.getKey() + " may not be the null key"));
257             } else if (conceptEntry.getValue() == null) {
258                 result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
259                         "value on concept entry " + conceptEntry.getKey() + " may not be null"));
260             } else if (!conceptEntry.getKey().equals(conceptEntry.getValue().getKey())) {
261                 result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
262                         "key on concept entry key " + conceptEntry.getKey() + " does not equal concept value key "
263                                 + conceptEntry.getValue().getKey()));
264                 result = conceptEntry.getValue().validate(result);
265             } else {
266                 result = conceptEntry.getValue().validate(result);
267             }
268         }
269         return result;
270     }
271
272     @Override
273     public int compareTo(final PfConcept otherConcept) {
274         if (otherConcept == null) {
275             return -1;
276         }
277         if (this == otherConcept) {
278             return 0;
279         }
280         if (getClass() != otherConcept.getClass()) {
281             return getClass().getName().compareTo(otherConcept.getClass().getName());
282         }
283
284         @SuppressWarnings("unchecked")
285         final PfConceptContainer<C, A> other = (PfConceptContainer<C, A>) otherConcept;
286         int retVal = key.compareTo(other.key);
287         if (retVal != 0) {
288             return retVal;
289         }
290
291         if (!conceptMap.equals(other.conceptMap)) {
292             return (conceptMap.hashCode() - other.conceptMap.hashCode());
293         }
294
295         return 0;
296     }
297
298     @Override
299     public C get(final PfConceptKey conceptKey) {
300         return new PfConceptGetterImpl<>((NavigableMap<PfConceptKey, C>) conceptMap).get(conceptKey);
301     }
302
303     @Override
304     public C get(final String conceptKeyName) {
305         return new PfConceptGetterImpl<>((NavigableMap<PfConceptKey, C>) conceptMap).get(conceptKeyName);
306     }
307
308     @Override
309     public C get(final String conceptKeyName, final String conceptKeyVersion) {
310         return new PfConceptGetterImpl<>((NavigableMap<PfConceptKey, C>) conceptMap).get(conceptKeyName,
311                 conceptKeyVersion);
312     }
313
314     @Override
315     public Set<C> getAll(final String conceptKeyName) {
316         return new PfConceptGetterImpl<>((NavigableMap<PfConceptKey, C>) conceptMap).getAll(conceptKeyName);
317     }
318
319     @Override
320     public Set<C> getAll(final String conceptKeyName, final String conceptKeyVersion) {
321         return new PfConceptGetterImpl<>((NavigableMap<PfConceptKey, C>) conceptMap).getAll(conceptKeyName,
322                 conceptKeyVersion);
323     }
324
325     /**
326      * Get a new empty instance of a concept for this concept map.
327      *
328      * @return the new instance
329      */
330     @SuppressWarnings("unchecked")
331     private C getConceptNewInstance() {
332         try {
333             String conceptClassName =
334                     ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
335             return (C) Class.forName(conceptClassName).getDeclaredConstructor().newInstance();
336         } catch (Exception ex) {
337             throw new PfModelRuntimeException(Response.Status.INTERNAL_SERVER_ERROR,
338                     "failed to instantiate instance of container concept class", ex);
339         }
340     }
341 }