2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2020-2021 Nordix Foundation.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
20 * SPDX-License-Identifier: Apache-2.0
21 * ============LICENSE_END=========================================================
24 package org.onap.policy.pdp.xacml.application.common.matchable;
26 import java.util.HashMap;
28 import java.util.Map.Entry;
29 import java.util.function.Function;
31 import lombok.NonNull;
32 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
33 import org.onap.policy.models.tosca.authorative.concepts.ToscaDataType;
34 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType;
35 import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty;
36 import org.onap.policy.models.tosca.authorative.concepts.ToscaSchemaDefinition;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
41 public class MatchablePolicyType {
42 private static final Logger LOGGER = LoggerFactory.getLogger(MatchablePolicyType.class);
44 public static final String TOSCA_PRIMITIVE_STRING = "string";
45 public static final String TOSCA_PRIMITIVE_INTEGER = "integer";
46 public static final String TOSCA_PRIMITIVE_FLOAT = "float";
47 public static final String TOSCA_PRIMITIVE_BOOLEAN = "boolean";
48 public static final String TOSCA_PRIMITIVE_TIMESTAMP = "timestamp";
49 public static final String TOSCA_TYPE_LIST = "list";
50 public static final String TOSCA_TYPE_MAP = "map";
53 private static final Map<String, Function<ToscaProperty, MatchablePropertyTypeBase<?>>>
54 mapPrimitivesProperty = Map.of(
55 TOSCA_PRIMITIVE_STRING, MatchablePropertyTypeString::new,
56 TOSCA_PRIMITIVE_INTEGER, MatchablePropertyTypeInteger::new,
57 TOSCA_PRIMITIVE_FLOAT, MatchablePropertyTypeFloat::new,
58 TOSCA_PRIMITIVE_BOOLEAN, MatchablePropertyTypeBoolean::new,
59 TOSCA_PRIMITIVE_TIMESTAMP, MatchablePropertyTypeTimestamp::new
62 private static final Map<String, Function<ToscaSchemaDefinition, MatchablePropertyTypeBase<?>>>
63 mapPrimitivesSchema = Map.of(
64 TOSCA_PRIMITIVE_STRING, MatchablePropertyTypeString::new,
65 TOSCA_PRIMITIVE_INTEGER, MatchablePropertyTypeInteger::new,
66 TOSCA_PRIMITIVE_FLOAT, MatchablePropertyTypeFloat::new,
67 TOSCA_PRIMITIVE_BOOLEAN, MatchablePropertyTypeBoolean::new,
68 TOSCA_PRIMITIVE_TIMESTAMP, MatchablePropertyTypeTimestamp::new
72 ToscaConceptIdentifier policyId;
73 Map<String, MatchableProperty> matchables = new HashMap<>();
75 public MatchablePolicyType(@NonNull ToscaPolicyType policyType, @NonNull MatchableCallback callback) {
76 this.policyId = new ToscaConceptIdentifier(policyType.getName(), policyType.getVersion());
77 scanPolicyType(policyType, callback);
80 public MatchableProperty get(@NonNull String property) {
81 return this.matchables.get(property);
84 protected void scanPolicyType(@NonNull ToscaPolicyType inPolicyType, @NonNull MatchableCallback callback) {
85 ToscaPolicyType policyType = inPolicyType;
86 while (policyType != null) {
87 LOGGER.info("Scanning PolicyType {}:{}", policyType.getName(), policyType.getVersion());
89 // Scan for all the matchable properties
91 scanProperties(policyType.getProperties(), matchables, callback);
93 // Does this PolicyType derive from another Policy Type?
95 if ("tosca.policies.Root".equals(policyType.getDerivedFrom())) {
99 LOGGER.info("Found root - done scanning");
103 // Move to the parent policy and scan it for matchables.
105 policyType = callback.retrievePolicyType(policyType.getDerivedFrom());
110 * Scans properties for matchables.
112 * @param properties Map of ToscaProperties to scan
113 * @param matchables Found matchables will be put into this list
114 * @param callback Callback routine for finding Policy Types and Data Types
116 public static void scanProperties(Map<String, ToscaProperty> properties, Map<String, MatchableProperty> matchables,
117 MatchableCallback callback) {
118 for (Entry<String, ToscaProperty> entrySet : properties.entrySet()) {
119 final String property = entrySet.getKey();
120 final ToscaProperty toscaProperty = entrySet.getValue();
122 // Most likely case is its a primitive
124 if (isPrimitive(toscaProperty.getType())) {
125 MatchableProperty primitiveProperty = handlePrimitive(property, toscaProperty);
126 if (primitiveProperty != null) {
127 matchables.put(property, primitiveProperty);
129 } else if (TOSCA_TYPE_LIST.equals(toscaProperty.getType())) {
130 MatchableProperty listProperty = handleList(property, toscaProperty, matchables, callback);
131 if (listProperty != null) {
132 matchables.put(property, listProperty);
134 } else if (TOSCA_TYPE_MAP.equals(toscaProperty.getType())) {
135 MatchableProperty mapProperty = handleMap(property, toscaProperty, matchables, callback);
136 if (mapProperty != null) {
137 matchables.put(property, mapProperty);
140 scanDatatype(toscaProperty, matchables, callback);
146 * handlePrimitive - handles a primitive type only if its matchable.
148 * @param property String containing property name
149 * @param toscaProperty ToscaProperty object
150 * @return MatchableProperty object
152 public static MatchableProperty handlePrimitive(String property, ToscaProperty toscaProperty) {
153 if (!isMatchable(toscaProperty)) {
156 Function<ToscaProperty, MatchablePropertyTypeBase<?>> function =
157 mapPrimitivesProperty.get(toscaProperty.getType());
158 if (function != null) {
159 return new MatchableProperty(property, function.apply(toscaProperty));
161 throw new IllegalArgumentException("Not a primitive " + toscaProperty.getType());
165 * handlePrimitive from a schema. Note that a ToscaEntrySchema does NOT have a metadata section
166 * so you cannot check if its matchable.
168 * @param property String containing property name
169 * @param toscaSchema ToscaSchema
170 * @return MatchableProperty object
172 public static MatchableProperty handlePrimitive(String property, ToscaSchemaDefinition toscaSchema) {
173 Function<ToscaSchemaDefinition, MatchablePropertyTypeBase<?>> function =
174 mapPrimitivesSchema.get(toscaSchema.getType());
175 if (function != null) {
176 return new MatchableProperty(property, function.apply(toscaSchema));
178 throw new IllegalArgumentException("Not a primitive " + toscaSchema.getType());
182 * handleList - iterates a list looking for matchables.
184 * @param property String containing property name
185 * @param toscaProperty ToscaProperty object
186 * @param matchables list of matchables to add to
187 * @param callback MatchableCallback to retrieve Data Types
188 * @return MatchableProperty object
190 public static MatchableProperty handleList(@NonNull String property, @NonNull ToscaProperty toscaProperty,
191 Map<String, MatchableProperty> matchables,
192 @NonNull MatchableCallback callback) {
193 if (!TOSCA_TYPE_LIST.equals(toscaProperty.getType())) {
194 throw new IllegalArgumentException("must be a list");
197 // Is it a simple or complex list?
199 if (isPrimitive(toscaProperty.getEntrySchema().getType())) {
201 // Only if the list is matchable
203 if (!isMatchable(toscaProperty)) {
207 // Simple list of primitives
209 return new MatchableProperty(property, new MatchablePropertyTypeList(toscaProperty));
212 // Scan the datatype for matchables
214 scanDatatype(toscaProperty.getEntrySchema(), matchables, callback);
216 // Return nothing - scanning the datatype should find the matchables
222 * handleMap - iterates a map looking for matchables.
224 * @param property String containing property name
225 * @param toscaProperty ToscaProperty object
226 * @param matchables list of matchables to add to
227 * @param callback MatchableCallback to retrieve Data Types
228 * @return MatchableProperty object
230 public static MatchableProperty handleMap(@NonNull String property, @NonNull ToscaProperty toscaProperty,
231 Map<String, MatchableProperty> matchables,
232 @NonNull MatchableCallback callback) {
233 if (!TOSCA_TYPE_MAP.equals(toscaProperty.getType())) {
234 throw new IllegalArgumentException("must be a map");
237 // Is it a simple or complex map?
239 if (isPrimitive(toscaProperty.getEntrySchema().getType())) {
241 // Only if the map is matchable
243 if (!isMatchable(toscaProperty)) {
247 // Simple map of primitives
249 return new MatchableProperty(property, new MatchablePropertyTypeMap(toscaProperty));
252 // Scan the datatype for matchables
254 scanDatatype(toscaProperty.getEntrySchema(), matchables, callback);
256 // Return nothing - scanning the datatype should find the matchables
262 * scanDatatype - scans a datatypes schema properties for matchables.
264 * @param toscaProperty ToscaProperty object
265 * @param matchables list of matchables to add to
266 * @param callback MatchableCallback to retrieve Data Types
268 public static void scanDatatype(@NonNull ToscaProperty toscaProperty, Map<String, MatchableProperty> matchables,
269 @NonNull MatchableCallback callback) {
271 // Don't check for matchable property, as that does not make sense to support right now. But we need to
272 // scan the datatype for matchable properties.
274 LOGGER.info("Retrieving datatype {}", toscaProperty.getType());
276 // Try to retrieve the datatype
278 ToscaDataType dataType = callback.retrieveDataType(toscaProperty.getType());
279 if (dataType == null) {
280 LOGGER.error("Failed to retrieve datatype {}", toscaProperty.getType());
284 // Now scan the properties in the datatype
286 scanProperties(dataType.getProperties(), matchables, callback);
290 * scanDatatypes - scans a datatype schema for matchables.
292 * @param toscaSchema ToscaEntrySchema
293 * @param matchables list of matchables to add to
294 * @param callback MatchableCallback object for retrieving other data types
296 public static void scanDatatype(@NonNull ToscaSchemaDefinition toscaSchema,
297 Map<String, MatchableProperty> matchables,
298 @NonNull MatchableCallback callback) {
300 // Don't check for matchable property, as that does not make sense to support right now. But we need to
301 // scan the datatype for matchable properties.
303 LOGGER.info("Retrieving datatype {}", toscaSchema.getType());
305 // Try to retrieve the datatype
307 ToscaDataType dataType = callback.retrieveDataType(toscaSchema.getType());
308 if (dataType == null) {
309 LOGGER.error("Failed to retrieve datatype {}", toscaSchema.getType());
313 // Now scan the properties in the datatype
315 scanProperties(dataType.getProperties(), matchables, callback);
321 * @param type String containing type
322 * @return true if it is a primitive
324 public static boolean isPrimitive(@NonNull String type) {
325 return mapPrimitivesProperty.containsKey(type);
329 * isMatchable - scans metadata for matchable field set to true.
331 * @param property ToscaProperty object
332 * @return true if matchable
334 public static boolean isMatchable(@NonNull ToscaProperty property) {
335 if (property.getMetadata() == null) {
338 String isMatchable = property.getMetadata().get("matchable");
339 if (isMatchable == null) {
342 return "true".equalsIgnoreCase(isMatchable);