2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 * SPDX-License-Identifier: Apache-2.0
20 * ============LICENSE_END=========================================================
23 package org.onap.policy.pdp.xacml.application.common.matchable;
25 import java.util.HashMap;
27 import java.util.Map.Entry;
28 import java.util.function.Function;
30 import lombok.NonNull;
31 import org.onap.policy.models.tosca.authorative.concepts.ToscaDataType;
32 import org.onap.policy.models.tosca.authorative.concepts.ToscaEntrySchema;
33 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier;
34 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType;
35 import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 public class MatchablePolicyType {
41 private static final Logger LOGGER = LoggerFactory.getLogger(MatchablePolicyType.class);
43 public static final String TOSCA_PRIMITIVE_STRING = "string";
44 public static final String TOSCA_PRIMITIVE_INTEGER = "integer";
45 public static final String TOSCA_PRIMITIVE_FLOAT = "float";
46 public static final String TOSCA_PRIMITIVE_BOOLEAN = "boolean";
47 public static final String TOSCA_PRIMITIVE_TIMESTAMP = "timestamp";
48 public static final String TOSCA_TYPE_LIST = "list";
49 public static final String TOSCA_TYPE_MAP = "map";
51 private static final Map<String, Function<ToscaProperty, MatchablePropertyTypeBase<?>>>
52 mapPrimitivesProperty = Map.of(
53 TOSCA_PRIMITIVE_STRING, MatchablePropertyTypeString::new,
54 TOSCA_PRIMITIVE_INTEGER, MatchablePropertyTypeInteger::new,
55 TOSCA_PRIMITIVE_FLOAT, MatchablePropertyTypeFloat::new,
56 TOSCA_PRIMITIVE_BOOLEAN, MatchablePropertyTypeBoolean::new,
57 TOSCA_PRIMITIVE_TIMESTAMP, MatchablePropertyTypeTimestamp::new
60 private static final Map<String, Function<ToscaEntrySchema, MatchablePropertyTypeBase<?>>>
61 mapPrimitivesSchema = Map.of(
62 TOSCA_PRIMITIVE_STRING, MatchablePropertyTypeString::new,
63 TOSCA_PRIMITIVE_INTEGER, MatchablePropertyTypeInteger::new,
64 TOSCA_PRIMITIVE_FLOAT, MatchablePropertyTypeFloat::new,
65 TOSCA_PRIMITIVE_BOOLEAN, MatchablePropertyTypeBoolean::new,
66 TOSCA_PRIMITIVE_TIMESTAMP, MatchablePropertyTypeTimestamp::new
69 ToscaPolicyIdentifier policyId;
70 Map<String, MatchableProperty> matchables = new HashMap<>();
72 public MatchablePolicyType(@NonNull ToscaPolicyType policyType, @NonNull MatchableCallback callback) {
73 this.policyId = new ToscaPolicyIdentifier(policyType.getName(), policyType.getVersion());
74 scanPolicyType(policyType, callback);
77 public MatchableProperty get(@NonNull String property) {
78 return this.matchables.get(property);
81 protected void scanPolicyType(@NonNull ToscaPolicyType inPolicyType, @NonNull MatchableCallback callback) {
82 ToscaPolicyType policyType = inPolicyType;
83 while (policyType != null) {
84 LOGGER.info("Scanning PolicyType {}:{}", policyType.getName(), policyType.getVersion());
86 // Scan for all the matchable properties
88 scanProperties(policyType.getProperties(), matchables, callback);
90 // Does this PolicyType derive from another Policy Type?
92 if ("tosca.policies.Root".equals(policyType.getDerivedFrom())) {
96 LOGGER.info("Found root - done scanning");
100 // Move to the parent policy and scan it for matchables.
102 policyType = callback.retrievePolicyType(policyType.getDerivedFrom());
107 * Scans properties for matchables.
109 * @param properties Map of ToscaProperties to scan
110 * @param matchables Found matchables will be put into this list
111 * @param callback Callback routine for finding Policy Types and Data Types
113 public static void scanProperties(Map<String, ToscaProperty> properties, Map<String, MatchableProperty> matchables,
114 MatchableCallback callback) {
115 for (Entry<String, ToscaProperty> entrySet : properties.entrySet()) {
116 final String property = entrySet.getKey();
117 final ToscaProperty toscaProperty = entrySet.getValue();
119 // Most likely case is its a primitive
121 if (isPrimitive(toscaProperty.getType())) {
122 MatchableProperty primitiveProperty = handlePrimitive(property, toscaProperty);
123 if (primitiveProperty != null) {
124 matchables.put(property, primitiveProperty);
126 } else if (TOSCA_TYPE_LIST.equals(toscaProperty.getType())) {
127 MatchableProperty listProperty = handleList(property, toscaProperty, matchables, callback);
128 if (listProperty != null) {
129 matchables.put(property, listProperty);
131 } else if (TOSCA_TYPE_MAP.equals(toscaProperty.getType())) {
132 MatchableProperty mapProperty = handleMap(property, toscaProperty, matchables, callback);
133 if (mapProperty != null) {
134 matchables.put(property, mapProperty);
137 scanDatatype(toscaProperty, matchables, callback);
143 * handlePrimitive - handles a primitive type only if its matchable.
145 * @param property String containing property name
146 * @param toscaProperty ToscaProperty object
147 * @return MatchableProperty object
149 public static MatchableProperty handlePrimitive(String property, ToscaProperty toscaProperty) {
150 if (!isMatchable(toscaProperty)) {
153 Function<ToscaProperty, MatchablePropertyTypeBase<?>> function =
154 mapPrimitivesProperty.get(toscaProperty.getType());
155 if (function != null) {
156 return new MatchableProperty(property, function.apply(toscaProperty));
158 throw new IllegalArgumentException("Not a primitive " + toscaProperty.getType());
162 * handlePrimitive from a schema. Note that a ToscaEntrySchema does NOT have a metadata section
163 * so you cannot check if its matchable.
165 * @param property String containing property name
166 * @param toscaSchema ToscaSchema
167 * @return MatchableProperty object
169 public static MatchableProperty handlePrimitive(String property, ToscaEntrySchema toscaSchema) {
170 Function<ToscaEntrySchema, MatchablePropertyTypeBase<?>> function =
171 mapPrimitivesSchema.get(toscaSchema.getType());
172 if (function != null) {
173 return new MatchableProperty(property, function.apply(toscaSchema)); // compilation err wants ToscaProperty
175 throw new IllegalArgumentException("Not a primitive " + toscaSchema.getType());
179 * handleList - iterates a list looking for matchables.
181 * @param property String containing property name
182 * @param toscaProperty ToscaProperty object
183 * @param matchables list of matchables to add to
184 * @param callback MatchableCallback to retrieve Data Types
185 * @return MatchableProperty object
187 public static MatchableProperty handleList(@NonNull String property, @NonNull ToscaProperty toscaProperty,
188 Map<String, MatchableProperty> matchables, @NonNull MatchableCallback callback) {
189 if (! TOSCA_TYPE_LIST.equals(toscaProperty.getType())) {
190 throw new IllegalArgumentException("must be a list");
193 // Is it a simple or complex list?
195 if (isPrimitive(toscaProperty.getEntrySchema().getType())) {
197 // Only if the list is matchable
199 if (!isMatchable(toscaProperty)) {
203 // Simple list of primitives
205 return new MatchableProperty(property, new MatchablePropertyTypeList(toscaProperty));
208 // Scan the datatype for matchables
210 scanDatatype(toscaProperty.getEntrySchema(), matchables, callback);
212 // Return nothing - scanning the datatype should find the matchables
218 * handleMap - iterates a map looking for matchables.
220 * @param property String containing property name
221 * @param toscaProperty ToscaProperty object
222 * @param matchables list of matchables to add to
223 * @param callback MatchableCallback to retrieve Data Types
224 * @return MatchableProperty object
226 public static MatchableProperty handleMap(@NonNull String property, @NonNull ToscaProperty toscaProperty,
227 Map<String, MatchableProperty> matchables, @NonNull MatchableCallback callback) {
228 if (! TOSCA_TYPE_MAP.equals(toscaProperty.getType())) {
229 throw new IllegalArgumentException("must be a map");
232 // Is it a simple or complex map?
234 if (isPrimitive(toscaProperty.getEntrySchema().getType())) {
236 // Only if the map is matchable
238 if (!isMatchable(toscaProperty)) {
242 // Simple map of primitives
244 return new MatchableProperty(property, new MatchablePropertyTypeMap(toscaProperty));
247 // Scan the datatype for matchables
249 scanDatatype(toscaProperty.getEntrySchema(), matchables, callback);
251 // Return nothing - scanning the datatype should find the matchables
257 * scanDatatype - scans a datatypes schema properties for matchables.
259 * @param toscaProperty ToscaProperty object
260 * @param matchables list of matchables to add to
261 * @param callback MatchableCallback to retrieve Data Types
263 public static void scanDatatype(@NonNull ToscaProperty toscaProperty, Map<String, MatchableProperty> matchables,
264 @NonNull MatchableCallback callback) {
266 // Don't check for matchable property, as that does not make sense to support right now. But we need to
267 // scan the datatype for matchable properties.
269 LOGGER.info("Retrieving datatype {}", toscaProperty.getType());
271 // Try to retrieve the datatype
273 ToscaDataType dataType = callback.retrieveDataType(toscaProperty.getType());
274 if (dataType == null) {
275 LOGGER.error("Failed to retrieve datatype {}", toscaProperty.getType());
279 // Now scan the properties in the datatype
281 scanProperties(dataType.getProperties(), matchables, callback);
285 * scanDatatypes - scans a datatype schema for matchables.
287 * @param toscaSchema ToscaEntrySchema
288 * @param matchables list of matchables to add to
289 * @param callback MatchableCallback object for retrieving other data types
291 public static void scanDatatype(@NonNull ToscaEntrySchema toscaSchema, Map<String, MatchableProperty> matchables,
292 @NonNull MatchableCallback callback) {
294 // Don't check for matchable property, as that does not make sense to support right now. But we need to
295 // scan the datatype for matchable properties.
297 LOGGER.info("Retrieving datatype {}", toscaSchema.getType());
299 // Try to retrieve the datatype
301 ToscaDataType dataType = callback.retrieveDataType(toscaSchema.getType());
302 if (dataType == null) {
303 LOGGER.error("Failed to retrieve datatype {}", toscaSchema.getType());
307 // Now scan the properties in the datatype
309 scanProperties(dataType.getProperties(), matchables, callback);
315 * @param type String containing type
316 * @return true if it is a primitive
318 public static boolean isPrimitive(@NonNull String type) {
319 return mapPrimitivesProperty.containsKey(type);
323 * isMatchable - scans metadata for matchable field set to true.
325 * @param property ToscaProperty object
326 * @return true if matchable
328 public static boolean isMatchable(@NonNull ToscaProperty property) {
329 if (property.getMetadata() == null) {
332 String isMatchable = property.getMetadata().get("matchable");
333 if (isMatchable == null) {
336 return "true".equalsIgnoreCase(isMatchable);