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";
52 private static final Map<String, Function<ToscaProperty, MatchablePropertyTypeBase<?>>>
53 mapPrimitivesProperty = Map.of(
54 TOSCA_PRIMITIVE_STRING, MatchablePropertyTypeString::new,
55 TOSCA_PRIMITIVE_INTEGER, MatchablePropertyTypeInteger::new,
56 TOSCA_PRIMITIVE_FLOAT, MatchablePropertyTypeFloat::new,
57 TOSCA_PRIMITIVE_BOOLEAN, MatchablePropertyTypeBoolean::new,
58 TOSCA_PRIMITIVE_TIMESTAMP, MatchablePropertyTypeTimestamp::new
61 private static final Map<String, Function<ToscaEntrySchema, MatchablePropertyTypeBase<?>>>
62 mapPrimitivesSchema = Map.of(
63 TOSCA_PRIMITIVE_STRING, MatchablePropertyTypeString::new,
64 TOSCA_PRIMITIVE_INTEGER, MatchablePropertyTypeInteger::new,
65 TOSCA_PRIMITIVE_FLOAT, MatchablePropertyTypeFloat::new,
66 TOSCA_PRIMITIVE_BOOLEAN, MatchablePropertyTypeBoolean::new,
67 TOSCA_PRIMITIVE_TIMESTAMP, MatchablePropertyTypeTimestamp::new
71 ToscaPolicyIdentifier policyId;
72 Map<String, MatchableProperty> matchables = new HashMap<>();
74 public MatchablePolicyType(@NonNull ToscaPolicyType policyType, @NonNull MatchableCallback callback) {
75 this.policyId = new ToscaPolicyIdentifier(policyType.getName(), policyType.getVersion());
76 scanPolicyType(policyType, callback);
79 public MatchableProperty get(@NonNull String property) {
80 return this.matchables.get(property);
83 protected void scanPolicyType(@NonNull ToscaPolicyType inPolicyType, @NonNull MatchableCallback callback) {
84 ToscaPolicyType policyType = inPolicyType;
85 while (policyType != null) {
86 LOGGER.info("Scanning PolicyType {}:{}", policyType.getName(), policyType.getVersion());
88 // Scan for all the matchable properties
90 scanProperties(policyType.getProperties(), matchables, callback);
92 // Does this PolicyType derive from another Policy Type?
94 if ("tosca.policies.Root".equals(policyType.getDerivedFrom())) {
98 LOGGER.info("Found root - done scanning");
102 // Move to the parent policy and scan it for matchables.
104 policyType = callback.retrievePolicyType(policyType.getDerivedFrom());
109 * Scans properties for matchables.
111 * @param properties Map of ToscaProperties to scan
112 * @param matchables Found matchables will be put into this list
113 * @param callback Callback routine for finding Policy Types and Data Types
115 public static void scanProperties(Map<String, ToscaProperty> properties, Map<String, MatchableProperty> matchables,
116 MatchableCallback callback) {
117 for (Entry<String, ToscaProperty> entrySet : properties.entrySet()) {
118 final String property = entrySet.getKey();
119 final ToscaProperty toscaProperty = entrySet.getValue();
121 // Most likely case is its a primitive
123 if (isPrimitive(toscaProperty.getType())) {
124 MatchableProperty primitiveProperty = handlePrimitive(property, toscaProperty);
125 if (primitiveProperty != null) {
126 matchables.put(property, primitiveProperty);
128 } else if (TOSCA_TYPE_LIST.equals(toscaProperty.getType())) {
129 MatchableProperty listProperty = handleList(property, toscaProperty, matchables, callback);
130 if (listProperty != null) {
131 matchables.put(property, listProperty);
133 } else if (TOSCA_TYPE_MAP.equals(toscaProperty.getType())) {
134 MatchableProperty mapProperty = handleMap(property, toscaProperty, matchables, callback);
135 if (mapProperty != null) {
136 matchables.put(property, mapProperty);
139 scanDatatype(toscaProperty, matchables, callback);
145 * handlePrimitive - handles a primitive type only if its matchable.
147 * @param property String containing property name
148 * @param toscaProperty ToscaProperty object
149 * @return MatchableProperty object
151 public static MatchableProperty handlePrimitive(String property, ToscaProperty toscaProperty) {
152 if (!isMatchable(toscaProperty)) {
155 Function<ToscaProperty, MatchablePropertyTypeBase<?>> function =
156 mapPrimitivesProperty.get(toscaProperty.getType());
157 if (function != null) {
158 return new MatchableProperty(property, function.apply(toscaProperty));
160 throw new IllegalArgumentException("Not a primitive " + toscaProperty.getType());
164 * handlePrimitive from a schema. Note that a ToscaEntrySchema does NOT have a metadata section
165 * so you cannot check if its matchable.
167 * @param property String containing property name
168 * @param toscaSchema ToscaSchema
169 * @return MatchableProperty object
171 public static MatchableProperty handlePrimitive(String property, ToscaEntrySchema toscaSchema) {
172 Function<ToscaEntrySchema, MatchablePropertyTypeBase<?>> function =
173 mapPrimitivesSchema.get(toscaSchema.getType());
174 if (function != null) {
175 return new MatchableProperty(property, function.apply(toscaSchema));
177 throw new IllegalArgumentException("Not a primitive " + toscaSchema.getType());
181 * handleList - iterates a list looking for matchables.
183 * @param property String containing property name
184 * @param toscaProperty ToscaProperty object
185 * @param matchables list of matchables to add to
186 * @param callback MatchableCallback to retrieve Data Types
187 * @return MatchableProperty object
189 public static MatchableProperty handleList(@NonNull String property, @NonNull ToscaProperty toscaProperty,
190 Map<String, MatchableProperty> matchables, @NonNull MatchableCallback callback) {
191 if (! TOSCA_TYPE_LIST.equals(toscaProperty.getType())) {
192 throw new IllegalArgumentException("must be a list");
195 // Is it a simple or complex list?
197 if (isPrimitive(toscaProperty.getEntrySchema().getType())) {
199 // Only if the list is matchable
201 if (!isMatchable(toscaProperty)) {
205 // Simple list of primitives
207 return new MatchableProperty(property, new MatchablePropertyTypeList(toscaProperty));
210 // Scan the datatype for matchables
212 scanDatatype(toscaProperty.getEntrySchema(), matchables, callback);
214 // Return nothing - scanning the datatype should find the matchables
220 * handleMap - iterates a map looking for matchables.
222 * @param property String containing property name
223 * @param toscaProperty ToscaProperty object
224 * @param matchables list of matchables to add to
225 * @param callback MatchableCallback to retrieve Data Types
226 * @return MatchableProperty object
228 public static MatchableProperty handleMap(@NonNull String property, @NonNull ToscaProperty toscaProperty,
229 Map<String, MatchableProperty> matchables, @NonNull MatchableCallback callback) {
230 if (! TOSCA_TYPE_MAP.equals(toscaProperty.getType())) {
231 throw new IllegalArgumentException("must be a map");
234 // Is it a simple or complex map?
236 if (isPrimitive(toscaProperty.getEntrySchema().getType())) {
238 // Only if the map is matchable
240 if (!isMatchable(toscaProperty)) {
244 // Simple map of primitives
246 return new MatchableProperty(property, new MatchablePropertyTypeMap(toscaProperty));
249 // Scan the datatype for matchables
251 scanDatatype(toscaProperty.getEntrySchema(), matchables, callback);
253 // Return nothing - scanning the datatype should find the matchables
259 * scanDatatype - scans a datatypes schema properties for matchables.
261 * @param toscaProperty ToscaProperty object
262 * @param matchables list of matchables to add to
263 * @param callback MatchableCallback to retrieve Data Types
265 public static void scanDatatype(@NonNull ToscaProperty toscaProperty, Map<String, MatchableProperty> matchables,
266 @NonNull MatchableCallback callback) {
268 // Don't check for matchable property, as that does not make sense to support right now. But we need to
269 // scan the datatype for matchable properties.
271 LOGGER.info("Retrieving datatype {}", toscaProperty.getType());
273 // Try to retrieve the datatype
275 ToscaDataType dataType = callback.retrieveDataType(toscaProperty.getType());
276 if (dataType == null) {
277 LOGGER.error("Failed to retrieve datatype {}", toscaProperty.getType());
281 // Now scan the properties in the datatype
283 scanProperties(dataType.getProperties(), matchables, callback);
287 * scanDatatypes - scans a datatype schema for matchables.
289 * @param toscaSchema ToscaEntrySchema
290 * @param matchables list of matchables to add to
291 * @param callback MatchableCallback object for retrieving other data types
293 public static void scanDatatype(@NonNull ToscaEntrySchema toscaSchema, Map<String, MatchableProperty> matchables,
294 @NonNull MatchableCallback callback) {
296 // Don't check for matchable property, as that does not make sense to support right now. But we need to
297 // scan the datatype for matchable properties.
299 LOGGER.info("Retrieving datatype {}", toscaSchema.getType());
301 // Try to retrieve the datatype
303 ToscaDataType dataType = callback.retrieveDataType(toscaSchema.getType());
304 if (dataType == null) {
305 LOGGER.error("Failed to retrieve datatype {}", toscaSchema.getType());
309 // Now scan the properties in the datatype
311 scanProperties(dataType.getProperties(), matchables, callback);
317 * @param type String containing type
318 * @return true if it is a primitive
320 public static boolean isPrimitive(@NonNull String type) {
321 return mapPrimitivesProperty.containsKey(type);
325 * isMatchable - scans metadata for matchable field set to true.
327 * @param property ToscaProperty object
328 * @return true if matchable
330 public static boolean isMatchable(@NonNull ToscaProperty property) {
331 if (property.getMetadata() == null) {
334 String isMatchable = property.getMetadata().get("matchable");
335 if (isMatchable == null) {
338 return "true".equalsIgnoreCase(isMatchable);