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