cfc0e42a85338459a968b1bed9538bfaab758463
[clamp.git] / src / main / java / org / onap / clamp / clds / tosca / update / parser / ToscaConverterToJsonSchema.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP CLAMP
4  * ================================================================================
5  * Copyright (C) 2020 AT&T Intellectual Property. All rights
6  *                             reserved.
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
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
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.
19  * ============LICENSE_END============================================
20  * ===================================================================
21  *
22  */
23
24 package org.onap.clamp.clds.tosca.update.parser;
25
26 import com.google.gson.JsonArray;
27 import com.google.gson.JsonObject;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.LinkedHashMap;
31 import java.util.Map.Entry;
32 import org.onap.clamp.clds.tosca.update.elements.ToscaElement;
33 import org.onap.clamp.clds.tosca.update.elements.ToscaElementProperty;
34 import org.onap.clamp.clds.tosca.update.parser.metadata.ToscaMetadataParser;
35 import org.onap.clamp.clds.tosca.update.templates.JsonTemplate;
36
37 public class ToscaConverterToJsonSchema {
38     private LinkedHashMap<String, ToscaElement> components;
39     private LinkedHashMap<String, JsonTemplate> templates;
40
41     private ToscaMetadataParser metadataParser;
42
43     /**
44      * Constructor.
45      *
46      * @param toscaElementsMap    All the tosca elements found (policy type + data types + native tosca datatypes)
47      * @param jsonSchemaTemplates All Json schema templates to use
48      * @param metadataParser      The metadata parser to use for metadata section
49      */
50     public ToscaConverterToJsonSchema(LinkedHashMap<String, ToscaElement> toscaElementsMap,
51                                       LinkedHashMap<String, JsonTemplate> jsonSchemaTemplates,
52                                       ToscaMetadataParser metadataParser) {
53         this.components = toscaElementsMap;
54         this.templates = jsonSchemaTemplates;
55         this.metadataParser = metadataParser;
56     }
57
58     /**
59      * For a given component, launch process to parse it in Json.
60      *
61      * @param toscaElementKey name components
62      * @return return
63      */
64     public JsonObject getJsonSchemaOfToscaElement(String toscaElementKey) {
65         return this.getFieldAsObject(getToscaElement(toscaElementKey));
66     }
67
68     /**
69      * Return the classical/general fields of the component, & launch the properties deployment.
70      *
71      * @param toscaElement the compo
72      * @return a json object
73      */
74     public JsonObject getFieldAsObject(ToscaElement toscaElement) {
75
76         JsonObject globalFields = new JsonObject();
77         if (templates.get("object").hasFields("title")) {
78             globalFields.addProperty("title", toscaElement.getName());
79         }
80         if (templates.get("object").hasFields("type")) {
81             globalFields.addProperty("type", "object");
82         }
83         if (templates.get("object").hasFields("description")) {
84             if (toscaElement.getDescription() != null) {
85                 globalFields.addProperty("description", toscaElement.getDescription());
86             }
87         }
88         if (templates.get("object").hasFields("required")) {
89             globalFields.add("required", this.getRequirements(toscaElement.getName()));
90         }
91         if (templates.get("object").hasFields("properties")) {
92             globalFields.add("properties", this.deploy(toscaElement.getName()));
93         }
94         return globalFields;
95     }
96
97     /**
98      * Get the required properties of the Component, including the parents properties requirements.
99      *
100      * @param nameComponent name component
101      * @return a json array
102      */
103     public JsonArray getRequirements(String nameComponent) {
104         JsonArray requirements = new JsonArray();
105         ToscaElement toParse = components.get(nameComponent);
106         //Check for a father component, and launch the same process
107         if (!toParse.getDerivedFrom().equals("tosca.datatypes.Root")
108                 && !toParse.getDerivedFrom().equals("tosca.policies.Root")) {
109             requirements.addAll(getRequirements(toParse.getDerivedFrom()));
110         }
111         //Each property is checked, and add to the requirement array if it's required
112         Collection<ToscaElementProperty> properties = toParse.getProperties().values();
113         for (ToscaElementProperty toscaElementProperty : properties) {
114             if (toscaElementProperty.getItems().containsKey("required")
115                     && toscaElementProperty.getItems().get("required").equals(true)) {
116                 requirements.add(toscaElementProperty.getName());
117             }
118         }
119         return requirements;
120     }
121
122     /**
123      * The beginning of the recursive process. Get the parents (or not) to launch the same process, and otherwise
124      * deploy and parse the properties.
125      *
126      * @param nameComponent name component
127      * @return a json object
128      */
129     public JsonObject deploy(String nameComponent) {
130         JsonObject jsonSchema = new JsonObject();
131         ToscaElement toParse = components.get(nameComponent);
132         //Check for a father component, and launch the same process
133         if (!toParse.getDerivedFrom().equals("tosca.datatypes.Root")
134                 && !toParse.getDerivedFrom().equals("tosca.policies.Root")) {
135             jsonSchema = this.getParent(toParse.getDerivedFrom());
136         }
137         //For each component property, check if its a complex properties (a component) or not. In that case,
138         //launch the analyse of the property.
139         for (Entry<String, ToscaElementProperty> property : toParse.getProperties().entrySet()) {
140             if (getToscaElement((String) property.getValue().getItems().get("type")) != null) {
141                 jsonSchema.add(property.getValue().getName(),
142                         this.getJsonSchemaOfToscaElement((String) property.getValue().getItems().get("type")));
143             }
144             else {
145                 jsonSchema.add(property.getValue().getName(), this.complexParse(property.getValue()));
146             }
147         }
148         return jsonSchema;
149     }
150
151     /**
152      * If a component has a parent, it is deploy in the same way.
153      *
154      * @param nameComponent name component
155      * @return a json object
156      */
157     public JsonObject getParent(String nameComponent) {
158         return deploy(nameComponent);
159     }
160
161     /**
162      * to be done.
163      *
164      * @param toscaElementProperty property
165      * @return a json object
166      */
167     @SuppressWarnings("unchecked")
168     public JsonObject complexParse(ToscaElementProperty toscaElementProperty) {
169         JsonObject propertiesInJson = new JsonObject();
170         JsonTemplate currentPropertyJsonTemplate;
171         String typeProperty = (String) toscaElementProperty.getItems().get("type");
172         if (typeProperty.toLowerCase().equals("list") || typeProperty.toLowerCase().equals("map")) {
173             currentPropertyJsonTemplate = templates.get("object");
174         }
175         else {
176             String propertyType = (String) toscaElementProperty.getItems().get("type");
177             currentPropertyJsonTemplate = templates.get(propertyType.toLowerCase());
178         }
179         //Each "special" field is analysed, and has a specific treatment
180         for (String propertyField : toscaElementProperty.getItems().keySet()) {
181             switch (propertyField) {
182                 case "type":
183                     if (currentPropertyJsonTemplate.hasFields(propertyField)) {
184                         String fieldtype = (String) toscaElementProperty.getItems().get(propertyField);
185                         switch (fieldtype.toLowerCase()) {
186                             case "list":
187                                 propertiesInJson.addProperty("type", "array");
188                                 break;
189                             case "map":
190                                 propertiesInJson.addProperty("type", "object");
191                                 break;
192                             case "scalar-unit.time":
193                             case "scalar-unit.frequency":
194                             case "scalar-unit.size":
195                                 propertiesInJson.addProperty("type", "string");
196                                 break;
197                             case "timestamp":
198                                 propertiesInJson.addProperty("type", "string");
199                                 propertiesInJson.addProperty("format", "date-time");
200                                 break;
201                             case "float":
202                                 propertiesInJson.addProperty("type", "number");
203                                 break;
204                             case "range":
205                                 propertiesInJson.addProperty("type", "integer");
206                                 if (!checkConstraintPresence(toscaElementProperty, "greater_than")
207                                         && currentPropertyJsonTemplate.hasFields("exclusiveMinimum")) {
208                                     propertiesInJson.addProperty("exclusiveMinimum", false);
209                                 }
210                                 if (!checkConstraintPresence(toscaElementProperty, "less_than")
211                                         && currentPropertyJsonTemplate.hasFields("exclusiveMaximum")) {
212                                     propertiesInJson.addProperty("exclusiveMaximum", false);
213                                 }
214                                 break;
215                             default:
216                                 propertiesInJson.addProperty("type", currentPropertyJsonTemplate.getName());
217                                 break;
218                         }
219                     }
220                     break;
221                 case "metadata":
222                     if (metadataParser != null) {
223                         metadataParser.processAllMetadataElement(toscaElementProperty).entrySet()
224                                 .forEach((jsonEntry) -> {
225                                     propertiesInJson.add(jsonEntry.getKey(),
226                                             jsonEntry.getValue());
227
228                                 });
229                     }
230                     break;
231                 case "constraints":
232                     toscaElementProperty.addConstraintsAsJson(propertiesInJson,
233                             (ArrayList<Object>) toscaElementProperty.getItems().get("constraints"),
234                             currentPropertyJsonTemplate);
235                     break;
236                 case "entry_schema":
237                     //Here, a way to check if entry is a component (datatype) or a simple string
238                     if (getToscaElement(this.extractSpecificFieldFromMap(toscaElementProperty, "entry_schema"))
239                             != null) {
240                         String nameComponent = this.extractSpecificFieldFromMap(toscaElementProperty, "entry_schema");
241                         ToscaConverterToJsonSchema
242                                 child = new ToscaConverterToJsonSchema(components, templates,
243                                 metadataParser);
244                         JsonObject propertiesContainer = new JsonObject();
245
246                         switch ((String) toscaElementProperty.getItems().get("type")) {
247                             case "map": // Get it as an object
248                                 JsonObject componentAsProperty = child.getJsonSchemaOfToscaElement(nameComponent);
249                                 propertiesContainer.add(nameComponent, componentAsProperty);
250                                 if (currentPropertyJsonTemplate.hasFields("properties")) {
251                                     propertiesInJson.add("properties", propertiesContainer);
252                                 }
253                                 break;
254                             default://list : get it as an Array
255                                 JsonObject componentAsItem = child.getJsonSchemaOfToscaElement(nameComponent);
256                                 if (currentPropertyJsonTemplate.hasFields("properties")) {
257                                     propertiesInJson.add("items", componentAsItem);
258                                 }
259                                 break;
260                         }
261
262                     }
263                     // Native cases
264                     else if (toscaElementProperty.getItems().get("type").equals("list")) {
265                         JsonObject itemContainer = new JsonObject();
266                         String valueInEntrySchema =
267                                 this.extractSpecificFieldFromMap(toscaElementProperty, "entry_schema");
268                         itemContainer.addProperty("type", valueInEntrySchema);
269                         propertiesInJson.add("items", itemContainer);
270                     }
271                     // MAP Case, for now nothing
272
273                     break;
274                 default:
275                     //Each classical field : type, description, default..
276                     if (currentPropertyJsonTemplate.hasFields(propertyField) && !propertyField.equals("required")) {
277                         toscaElementProperty.addFieldToJson(propertiesInJson, propertyField,
278                                 toscaElementProperty.getItems().get(propertyField));
279                     }
280                     break;
281             }
282         }
283         return propertiesInJson;
284     }
285
286     /**
287      * Look for a matching Component for the name parameter, in the components list.
288      *
289      * @param name the tosca element name to search for
290      * @return a tosca element
291      */
292     public ToscaElement getToscaElement(String name) {
293         ToscaElement correspondingToscaElement = null;
294         if (components == null) {
295             return null;
296         }
297         for (ToscaElement toscaElement : components.values()) {
298             if (toscaElement.getName().equals(name)) {
299                 correspondingToscaElement = toscaElement;
300             }
301         }
302         return correspondingToscaElement;
303     }
304
305     /**
306      * Simple method to extract quickly a type field from particular property item.
307      *
308      * @param toscaElementProperty the property
309      * @param fieldName            the fieldname
310      * @return a string
311      */
312     @SuppressWarnings("unchecked")
313     public String extractSpecificFieldFromMap(ToscaElementProperty toscaElementProperty, String fieldName) {
314         LinkedHashMap<String, String> entrySchemaFields =
315                 (LinkedHashMap<String, String>) toscaElementProperty.getItems().get(fieldName);
316         return entrySchemaFields.get("type");
317     }
318
319     /**
320      * Check if a constraint, for a specific property, is there.
321      *
322      * @param toscaElementProperty property
323      * @param nameConstraint       name constraint
324      * @return a flag boolean
325      */
326     public boolean checkConstraintPresence(ToscaElementProperty toscaElementProperty, String nameConstraint) {
327         boolean presentConstraint = false;
328         if (toscaElementProperty.getItems().containsKey("constraints")) {
329             ArrayList<Object> constraints = (ArrayList) toscaElementProperty.getItems().get("constraints");
330             for (Object constraint : constraints) {
331                 if (constraint instanceof LinkedHashMap) {
332                     if (((LinkedHashMap) constraint).containsKey(nameConstraint)) {
333                         presentConstraint = true;
334                     }
335                 }
336             }
337         }
338         return presentConstraint;
339     }
340 }