2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2019 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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.models.base;
24 import java.lang.reflect.ParameterizedType;
25 import java.util.ArrayList;
26 import java.util.LinkedHashMap;
27 import java.util.List;
29 import java.util.Map.Entry;
30 import java.util.NavigableMap;
32 import java.util.TreeMap;
33 import javax.persistence.CascadeType;
34 import javax.persistence.EmbeddedId;
35 import javax.persistence.Entity;
36 import javax.persistence.FetchType;
37 import javax.persistence.ManyToMany;
38 import javax.persistence.Table;
39 import javax.ws.rs.core.Response;
41 import lombok.EqualsAndHashCode;
42 import lombok.NonNull;
43 import org.onap.policy.models.base.PfValidationResult.ValidationResult;
47 * This class is a concept container and holds a map of concepts. The {@link PfConceptContainer} class implements the
48 * helper methods of the {@link PfConceptGetter} interface to allow {@link PfConceptContainer} instances to be retrieved
49 * by calling methods directly on this class without referencing the contained map.
51 * <p>Validation checks that a container key is not null. An error is issued if no concepts are defined in a container.
52 * Each concept entry is checked to ensure that its key and value are not null and that the key matches the key in the
53 * map value. Each concept entry is then validated individually.
55 * @param C the concept being contained
59 @Table(name = "PfConceptContainer")
61 @EqualsAndHashCode(callSuper = false)
63 public class PfConceptContainer<C extends PfConcept, A extends PfNameVersion> extends PfConcept
64 implements PfConceptGetter<C>, PfAuthorative<List<Map<String, A>>> {
65 private static final long serialVersionUID = -324211738823208318L;
68 private PfConceptKey key;
70 @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
71 private Map<PfConceptKey, C> conceptMap;
74 * The Default Constructor creates a {@link PfConceptContainer} object with a null artifact key and creates an empty
77 public PfConceptContainer() {
78 this(new PfConceptKey());
82 * The Key Constructor creates a {@link PfConceptContainer} object with the given artifact key and creates an empty
85 * @param key the concept key
87 public PfConceptContainer(@NonNull final PfConceptKey key) {
88 this(key, new TreeMap<PfConceptKey, C>());
92 * This Constructor creates an concept container with all of its fields defined.
94 * @param key the concept container key
95 * @param conceptMap the concepts to be stored in the concept container
97 public PfConceptContainer(@NonNull final PfConceptKey key, @NonNull final Map<PfConceptKey, C> conceptMap) {
101 this.conceptMap = new TreeMap<>(conceptMap);
107 * @param copyConcept the concept to copy from
109 public PfConceptContainer(@NonNull final PfConceptContainer<C, A> copyConcept) {
111 this.key = new PfConceptKey(copyConcept.key);
113 this.conceptMap = new TreeMap<>();
114 for (final Entry<PfConceptKey, C> conceptMapEntry : copyConcept.conceptMap.entrySet()) {
115 PfConceptKey newK = new PfConceptKey(conceptMapEntry.getKey());
116 C newC = PfUtils.makeCopy(conceptMapEntry.getValue());
117 this.conceptMap.put(newK, newC);
122 public List<PfKey> getKeys() {
123 final List<PfKey> keyList = key.getKeys();
125 for (final C concept : conceptMap.values()) {
126 keyList.addAll(concept.getKeys());
133 public List<Map<String, A>> toAuthorative() {
134 // The returned list is a list of map singletons with one map for each map
135 // entry in the concept container
136 List<Map<String, A>> toscaPolicyMapList = new ArrayList<>();
138 for (Entry<PfConceptKey, C> conceptEntry : getConceptMap().entrySet()) {
139 // Create a map to hold this entry
140 Map<String, A> toscaPolicyMap = new LinkedHashMap<>(1);
142 // Add the concept container entry to the singleton map
143 @SuppressWarnings("unchecked")
144 PfAuthorative<A> authoritiveImpl = (PfAuthorative<A>) conceptEntry.getValue();
145 toscaPolicyMap.put(conceptEntry.getKey().getName(), authoritiveImpl.toAuthorative());
147 // Add the map to the returned list
148 toscaPolicyMapList.add(toscaPolicyMap);
151 return toscaPolicyMapList;
155 public void fromAuthorative(List<Map<String, A>> authorativeList) {
156 // Clear any existing map entries
159 // Concepts are in lists of maps
160 for (Map<String, A> incomingConceptMap : authorativeList) {
161 // Add the map entries one by one
162 for (Entry<String, A> incomingConceptEntry : incomingConceptMap.entrySet()) {
163 C jpaConcept = getConceptNewInstance();
165 // This cast allows us to call the fromAuthorative method
166 @SuppressWarnings("unchecked")
167 PfAuthorative<A> authoritiveImpl = (PfAuthorative<A>) jpaConcept;
169 if (incomingConceptEntry.getValue().getName() == null) {
170 incomingConceptEntry.getValue().setName(incomingConceptEntry.getKey());
173 // Set the key name and the rest of the values on the concept
174 authoritiveImpl.fromAuthorative(incomingConceptEntry.getValue());
176 // This cast gets the key of the concept
177 PfConceptKey conceptKey = (PfConceptKey) jpaConcept.getKey();
179 // Set the concept key of the concept
180 conceptKey.setName(incomingConceptEntry.getValue().getName());
182 if (incomingConceptEntry.getValue().getVersion() != null) {
183 conceptKey.setVersion(incomingConceptEntry.getValue().getVersion());
186 conceptKey.setVersion(PfKey.NULL_KEY_VERSION);
189 // After all that, save the map entry
190 conceptMap.put(conceptKey, jpaConcept);
194 if (conceptMap.isEmpty()) {
195 throw new PfModelRuntimeException(Response.Status.BAD_REQUEST,
196 "An incoming list of concepts must have at least one entry");
201 public void clean() {
203 for (final Entry<PfConceptKey, C> conceptEntry : conceptMap.entrySet()) {
204 conceptEntry.getKey().clean();
205 conceptEntry.getValue().clean();
210 public PfValidationResult validate(@NonNull final PfValidationResult resultIn) {
211 PfValidationResult result = resultIn;
213 if (key.equals(PfConceptKey.getNullKey())) {
214 result.addValidationMessage(
215 new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
218 result = key.validate(result);
220 if (conceptMap.isEmpty()) {
221 result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
222 "conceptMap may not be empty"));
224 result = validateConceptMap(result);
231 * Validate the concept map of the container.
233 * @param resultIn the incoming validation results so far
234 * @return the validation results with the results of this validation added
236 private PfValidationResult validateConceptMap(final PfValidationResult resultIn) {
237 PfValidationResult result = resultIn;
239 for (final Entry<PfConceptKey, C> conceptEntry : conceptMap.entrySet()) {
240 if (conceptEntry.getKey().equals(PfConceptKey.getNullKey())) {
241 result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
242 "key on concept entry " + conceptEntry.getKey() + " may not be the null key"));
244 if (conceptEntry.getValue() == null) {
245 result.addValidationMessage(new PfValidationMessage(key, this.getClass(), ValidationResult.INVALID,
246 "value on concept entry " + conceptEntry.getKey() + " may not be null"));
248 if (!conceptEntry.getKey().equals(conceptEntry.getValue().getKey())) {
249 result.addValidationMessage(new PfValidationMessage(key, this.getClass(),
250 ValidationResult.INVALID, "key on concept entry key " + conceptEntry.getKey()
251 + " does not equal concept value key " + conceptEntry.getValue().getKey()));
252 result = conceptEntry.getValue().validate(result);
254 result = conceptEntry.getValue().validate(result);
261 public int compareTo(final PfConcept otherConcept) {
262 if (otherConcept == null) {
265 if (this == otherConcept) {
268 if (getClass() != otherConcept.getClass()) {
269 return getClass().getName().compareTo(otherConcept.getClass().getName());
272 @SuppressWarnings("unchecked")
273 final PfConceptContainer<C, A> other = (PfConceptContainer<C, A>) otherConcept;
274 int retVal = key.compareTo(other.key);
279 if (!conceptMap.equals(other.conceptMap)) {
280 return (conceptMap.hashCode() - other.conceptMap.hashCode());
287 public C get(final PfConceptKey conceptKey) {
288 return new PfConceptGetterImpl<>((NavigableMap<PfConceptKey, C>) conceptMap).get(conceptKey);
292 public C get(final String conceptKeyName) {
293 return new PfConceptGetterImpl<>((NavigableMap<PfConceptKey, C>) conceptMap).get(conceptKeyName);
297 public C get(final String conceptKeyName, final String conceptKeyVersion) {
298 return new PfConceptGetterImpl<>((NavigableMap<PfConceptKey, C>) conceptMap).get(conceptKeyName,
303 public Set<C> getAll(final String conceptKeyName) {
304 return new PfConceptGetterImpl<>((NavigableMap<PfConceptKey, C>) conceptMap).getAll(conceptKeyName);
308 public Set<C> getAll(final String conceptKeyName, final String conceptKeyVersion) {
309 return new PfConceptGetterImpl<>((NavigableMap<PfConceptKey, C>) conceptMap).getAll(conceptKeyName,
314 * Get a new empty instance of a concept for this concept map.
316 * @return the new instance
318 @SuppressWarnings("unchecked")
319 private C getConceptNewInstance() {
321 String conceptClassName =
322 ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
323 return (C) Class.forName(conceptClassName).getDeclaredConstructor().newInstance();
324 } catch (Exception ex) {
325 throw new PfModelRuntimeException(Response.Status.INTERNAL_SERVER_ERROR,
326 "failed to instantiate instance of container concept class", ex);