Generated AAI Swagger file has schema violations
[aai/schema-service.git] / aai-schema-gen / src / main / java / org / onap / aai / schemagen / genxsd / YAMLfromOXM.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 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
10  * <p>
11  * http://www.apache.org/licenses/LICENSE-2.0
12  * <p>
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.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.aai.schemagen.genxsd;
22
23 import com.google.common.collect.Multimap;
24
25 import java.io.BufferedWriter;
26 import java.io.File;
27 import java.io.FileNotFoundException;
28 import java.io.IOException;
29 import java.nio.charset.Charset;
30 import java.nio.charset.StandardCharsets;
31 import java.nio.file.Files;
32 import java.nio.file.Path;
33 import java.nio.file.Paths;
34 import java.util.HashMap;
35 import java.util.LinkedHashSet;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.SortedSet;
39 import java.util.StringTokenizer;
40 import java.util.TreeMap;
41 import java.util.TreeSet;
42 import java.util.Vector;
43
44 import javax.xml.parsers.ParserConfigurationException;
45
46 import org.apache.commons.lang3.StringUtils;
47 import org.onap.aai.edges.EdgeIngestor;
48 import org.onap.aai.edges.EdgeRule;
49 import org.onap.aai.edges.EdgeRuleQuery;
50 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
51 import org.onap.aai.nodes.NodeIngestor;
52 import org.onap.aai.setup.SchemaConfigVersions;
53 import org.onap.aai.setup.SchemaVersion;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56 import org.w3c.dom.Element;
57 import org.w3c.dom.NodeList;
58 import org.xml.sax.SAXException;
59
60 public class YAMLfromOXM extends OxmFileProcessor {
61     private static final Logger logger = LoggerFactory.getLogger("YAMLfromOXM.class");
62     // private static StringBuffer totalPathSbAccumulator = new StringBuffer();
63     private static final String root = "../aai-schema/src/main/resources";
64     private static final String autoGenRoot = "aai-schema/src/main/resources";
65     private static final String generateTypeYAML = "yaml";
66     private static final String normalStartDir = "aai-schema-gen";
67     private static final String yaml_dir = (((System.getProperty("user.dir") != null)
68         && (!System.getProperty("user.dir").contains(normalStartDir))) ? autoGenRoot : root)
69         + "/aai_swagger_yaml";
70     private final String patchDefinePrefix = "zzzz-patch-";
71     private StringBuilder inventoryDefSb = null;
72
73     private String basePath;
74
75     public YAMLfromOXM(String basePath, SchemaConfigVersions schemaConfigVersions, NodeIngestor ni,
76         EdgeIngestor ei) {
77         super(schemaConfigVersions, ni, ei);
78         this.basePath = basePath;
79     }
80
81     public void setOxmVersion(File oxmFile, SchemaVersion v) {
82         super.setOxmVersion(oxmFile, v);
83     }
84
85     public void setXmlVersion(String xml, SchemaVersion v) {
86         super.setXmlVersion(xml, v);
87     }
88
89     public void setVersion(SchemaVersion v) {
90         super.setVersion(v);
91     }
92
93     @Override
94     public String getDocumentHeader() {
95         StringBuilder sb = new StringBuilder();
96         sb.append("#").append(LINE_SEPARATOR).append(
97             "# ============LICENSE_START=======================================================")
98             .append(LINE_SEPARATOR).append("# org.onap.aai").append(LINE_SEPARATOR)
99             .append(
100                 "# ================================================================================")
101             .append(LINE_SEPARATOR)
102             .append("# Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.")
103             .append(LINE_SEPARATOR)
104             .append(
105                 "# ================================================================================")
106             .append(LINE_SEPARATOR)
107             .append(
108                 "# Licensed under the Creative Commons License, Attribution 4.0 Intl. (the \"License\");")
109             .append(LINE_SEPARATOR)
110             .append("# you may not use this file except in compliance with the License.")
111             .append(LINE_SEPARATOR).append("# You may obtain a copy of the License at")
112             .append(LINE_SEPARATOR).append("# <p>").append(LINE_SEPARATOR)
113             .append("# https://creativecommons.org/licenses/by/4.0/").append(LINE_SEPARATOR)
114             .append("# <p>").append(LINE_SEPARATOR)
115             .append("# Unless required by applicable law or agreed to in writing, software")
116             .append(LINE_SEPARATOR)
117             .append("# distributed under the License is distributed on an \"AS IS\" BASIS,")
118             .append(LINE_SEPARATOR)
119             .append("# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.")
120             .append(LINE_SEPARATOR)
121             .append("# See the License for the specific language governing permissions and")
122             .append(LINE_SEPARATOR).append("# limitations under the License.")
123             .append(LINE_SEPARATOR)
124             .append(
125                 "# ============LICENSE_END=========================================================")
126             .append(LINE_SEPARATOR).append("#").append(LINE_SEPARATOR).append(LINE_SEPARATOR);
127         sb.append("swagger: \"2.0\"\ninfo:").append(LINE_SEPARATOR).append("  ");
128         sb.append("description: |");
129         if (versionSupportsSwaggerDiff(v.toString())) {
130             sb.append("\n    [Differences versus the previous schema version](" + "apidocs"
131                 + basePath + "/aai_swagger_" + v.toString() + ".diff)");
132         }
133         sb.append(DOUBLE_LINE_SEPARATOR)
134             .append("    This document is best viewed with Firefox or Chrome. ");
135         sb.append(
136             "Nodes can be found by opening the models link below and finding the node-type. ");
137         sb.append("Edge definitions can be found with the node definitions.").append(LINE_SEPARATOR)
138             .append("  version: \"").append(v.toString()).append("\"").append(LINE_SEPARATOR);
139         sb.append("  title: Active and Available Inventory REST API").append(LINE_SEPARATOR);
140         sb.append("  license:").append(LINE_SEPARATOR)
141             .append(
142                 "    name: Apache 2.0\n    url: http://www.apache.org/licenses/LICENSE-2.0.html")
143             .append(LINE_SEPARATOR);
144         sb.append("host: localhost").append(LINE_SEPARATOR).append("basePath: ").append(basePath)
145             .append("/").append(v.toString()).append(LINE_SEPARATOR);
146         sb.append("schemes:").append(LINE_SEPARATOR).append("  - https\npaths:")
147             .append(LINE_SEPARATOR);
148         return sb.toString();
149     }
150
151     protected void init() throws ParserConfigurationException, SAXException, IOException,
152         FileNotFoundException, EdgeRuleNotFoundException {
153         super.init();
154     }
155
156     @Override
157     public String process() throws ParserConfigurationException, SAXException, IOException,
158         FileNotFoundException, EdgeRuleNotFoundException {
159         StringBuilder sb = new StringBuilder();
160         StringBuilder pathSb = new StringBuilder();
161         try {
162             init();
163         } catch (Exception e) {
164             logger.error("Error initializing " + this.getClass(), e);
165             throw e;
166         }
167         pathSb.append(getDocumentHeader());
168         StringBuilder definitionsSb = new StringBuilder();
169         Element elem;
170         String javaTypeName;
171         combinedJavaTypes = new HashMap<>();
172         for (int i = 0; i < javaTypeNodes.getLength(); ++i) {
173             elem = (Element) javaTypeNodes.item(i);
174             javaTypeName = elem.getAttribute("name");
175             boolean processInventory = false;
176             if (!"Inventory".equals(javaTypeName)) {
177                 if (generatedJavaType.containsKey(getXmlRootElementName(javaTypeName))) {
178                     continue;
179                 }
180                 // will combine all matching java-types
181                 elem = getJavaTypeElementSwagger(javaTypeName);
182             } else {
183                 processInventory = true;
184             }
185
186             XSDElement javaTypeElement = new XSDElement(elem);
187
188             if (processInventory) {
189                 getTopLevelPaths(javaTypeElement);
190             }
191
192             if (javaTypeName == null) {
193                 String msg = "Invalid OXM file: <java-type> has no name attribute in " + oxmFile;
194                 logger.error(msg);
195                 throw new SAXException(msg);
196             }
197             namespaceFilter.add(getXmlRootElementName(javaTypeName));
198             processJavaTypeElementSwagger(javaTypeName, javaTypeElement, pathSb, definitionsSb,
199                 null, null, null, null, null, null);
200         }
201         sb.append(pathSb);
202
203         sb.append(appendDefinitions());
204         PutRelationPathSet prp = new PutRelationPathSet(v);
205         prp.generateRelations(ei);
206         return sb.toString();
207     }
208
209     public String appendDefinitions() {
210         return appendDefinitions(null);
211     }
212
213     public String appendDefinitions(Set<String> namespaceFilter) {
214         // append definitions
215         if (inventoryDefSb != null) {
216             javaTypeDefinitions.put("inventory", inventoryDefSb.toString());
217         }
218         StringBuilder sb = new StringBuilder("definitions:\n");
219         Map<String, String> sortedJavaTypeDefinitions =
220             new TreeMap<String, String>(javaTypeDefinitions);
221         for (Map.Entry<String, String> entry : sortedJavaTypeDefinitions.entrySet()) {
222             // logger.info("Key: "+entry.getKey()+"Value: "+ entry.getValue());
223             if (namespaceFilter != null && entry.getKey().matches("service-capabilities")) {
224                 for (String tally : namespaceFilter) {
225                     logger.debug("Marker: " + tally);
226                 }
227             }
228             if (namespaceFilter != null && (!namespaceFilter.contains(entry.getKey()))) {
229                 continue;
230             }
231             logger.debug(
232                 "Key: " + entry.getKey() + "Test: " + ("relationship-dict".equals(entry.getKey())));
233             if (entry.getKey().matches("relationship-dict")) {
234                 String jb = entry.getValue();
235                 logger.debug("Value: " + jb);
236                 int ndx = jb.indexOf("related-to-property:");
237                 if (ndx > 0) {
238                     jb = jb.substring(0, ndx);
239                     jb = StringUtils.stripEnd(jb, " ");
240                 }
241                 logger.debug("Value-after: " + jb);
242                 sb.append(jb);
243                 continue;
244             }
245             sb.append(entry.getValue());
246         }
247         return sb.toString();
248     }
249
250     private String getDictionary(String resource) {
251         StringBuilder dictSb = new StringBuilder();
252         dictSb.append("  ").append(resource).append(":\n");
253         dictSb.append("    description: |\n");
254         dictSb.append("      dictionary of ").append(resource).append("\n");
255         dictSb.append("    type: object\n");
256         dictSb.append("    properties:\n");
257         dictSb.append("      ").append(resource).append(":\n");
258         dictSb.append("        type: array\n");
259         dictSb.append("        items:\n");
260         dictSb.append("          $ref: \"#/definitions/").append(resource).append("-dict\"\n");
261         return dictSb.toString();
262     }
263
264     private String processJavaTypeElementSwagger(String javaTypeName, Element javaTypeElement,
265         StringBuilder pathSb, StringBuilder definitionsSb, String path, String tag, String opId,
266         String getItemName, StringBuilder pathParams, String validEdges) {
267
268         String xmlRootElementName = getXMLRootElementName(javaTypeElement);
269         StringBuilder definitionsLocalSb = new StringBuilder(256);
270         StringBuilder definitionsLocalPatchSb = new StringBuilder(256);
271
272         String useTag = null;
273         String useOpId = null;
274         logger.debug("tag=" + tag);
275         if (tag != null) {
276             // set ignore to true to skip Actions and Search
277             boolean topCheck = checkTopLevel(tag, true);
278             if (!topCheck) {
279                 return null;
280             }
281         }
282
283         if (!"Inventory".equals(javaTypeName)) {
284             if ("AaiInternal".equals(javaTypeName)) {
285                 return null;
286             }
287             if (opId == null) {
288                 useOpId = javaTypeName;
289             } else {
290                 useOpId = opId + javaTypeName;
291             }
292             if (tag == null) {
293                 useTag = javaTypeName;
294             }
295         }
296         path = "inventory".equals(xmlRootElementName) ? ""
297             : (path == null) ? "/" + xmlRootElementName : path + "/" + xmlRootElementName;
298         XSDJavaType javaType = new XSDJavaType(javaTypeElement);
299         if (getItemName != null) {
300             if ("array".equals(getItemName)) {
301                 return javaType.getArrayType();
302             } else {
303                 return javaType.getItemName();
304             }
305         }
306
307         NodeList parentNodes = javaTypeElement.getElementsByTagName("java-attributes");
308         if (parentNodes.getLength() == 0) {
309             logger.debug("no java-attributes for java-type " + javaTypeName);
310             return "";
311         }
312
313         String pathDescriptionProperty = javaType.getPathDescriptionProperty();
314         String container = javaType.getContainerProperty();
315         Vector<String> indexedProps = javaType.getIndexedProps();
316         Vector<String> dslStartNodeProps = javaType.getDslStartNodeProps();
317         Vector<String> containerProps = new Vector<String>();
318         if (container != null) {
319             logger.debug("javaTypeName " + javaTypeName + " container:" + container
320                 + " indexedProps:" + indexedProps);
321         }
322
323         Element parentElement = (Element) parentNodes.item(0);
324         NodeList xmlElementNodes = parentElement.getElementsByTagName("xml-element");
325
326         StringBuilder sbParameters = new StringBuilder();
327         StringBuilder sbPathParameters = new StringBuilder(); // separate naming path parameters
328                                                               // from name of parameter in the
329                                                               // schema
330         StringBuilder sbRequired = new StringBuilder();
331
332         int requiredCnt = 0;
333         int propertyCnt = 0;
334         StringBuilder sbProperties = new StringBuilder();
335         int patchPropertyCnt = 0; // manage payload properties separately for patch
336         StringBuilder sbPropertiesPatch = new StringBuilder();
337
338         if (appliedPaths.containsKey(path)) {
339             return null;
340         }
341
342         StringTokenizer st = new StringTokenizer(path, "/");
343         logger.debug("path: " + path + " st? " + st.toString());
344         if (st.countTokens() > 1 && getItemName == null) {
345             logger.debug("appliedPaths: " + appliedPaths + " containsKey? "
346                 + appliedPaths.containsKey(path));
347             appliedPaths.put(path, xmlRootElementName);
348         }
349
350         Vector<String> addTypeV = null;
351         String modifiedName;
352         String replaceDescription;
353         for (int i = 0; i < xmlElementNodes.getLength(); ++i) {
354             XSDElement xmlElementElement = new XSDElement((Element) xmlElementNodes.item(i));
355             if (!xmlElementElement.getParentNode().isSameNode(parentElement)) {
356                 continue;
357             }
358             String elementDescription = xmlElementElement.getPathDescriptionProperty();
359             if (getItemName == null) {
360                 addTypeV = xmlElementElement.getAddTypes(v.toString());
361             }
362             // use alternate name for parameter if already in the path string
363             modifiedName = "/{" + xmlElementElement.getAttribute("name") + "}";
364             if (path.contains(modifiedName)) {
365                 modifiedName = path.substring(path.lastIndexOf('/') + 1) + "."
366                     + xmlElementElement.getAttribute("name");
367             } else {
368                 modifiedName = xmlElementElement.getAttribute("name");
369             }
370             if ("true".equals(xmlElementElement.getAttribute("xml-key"))) {
371                 path += "/{" + modifiedName + "}";
372             }
373             logger.debug("path: " + path);
374             logger.debug("xmlElementElement.getAttribute(required):"
375                 + xmlElementElement.getAttribute("required"));
376
377             if ("true".equals(xmlElementElement.getAttribute("xml-key"))) {
378                 sbParameters.append(xmlElementElement.getPathParamYAML(elementDescription));
379                 sbPathParameters
380                     .append(xmlElementElement.getPathParamYAML(elementDescription, modifiedName));
381             }
382             if ("true".equals(xmlElementElement.getAttribute("required"))) {
383                 if (requiredCnt == 0) {
384                     sbRequired.append("    required:\n");
385                 }
386                 ++requiredCnt;
387                 if (addTypeV == null || addTypeV.isEmpty()) {
388                     sbRequired.append("    - ").append(xmlElementElement.getAttribute("name"))
389                         .append("\n");
390                 } else {
391                     for (int k = 0; k < addTypeV.size(); ++k) {
392                         sbRequired.append("    - ")
393                             .append(getXmlRootElementName(addTypeV.elementAt(k))).append(":\n");
394                     }
395                 }
396             }
397             if (indexedProps != null
398                 && indexedProps.contains(xmlElementElement.getAttribute("name"))) {
399                 containerProps.add(xmlElementElement.getQueryParamYAML());
400                 GetOperation.addContainerProps(container, containerProps);
401             }
402             if (xmlElementElement.isStandardType()) {
403                 boolean isDslStartNode =
404                     dslStartNodeProps.contains(xmlElementElement.getAttribute("name"));
405                 sbProperties.append(xmlElementElement.getTypePropertyYAML(isDslStartNode));
406                 if (!"resource-version".equals(xmlElementElement.getAttribute("name"))) {
407                     sbPropertiesPatch.append(xmlElementElement.getTypePropertyYAML(isDslStartNode));
408                     ++patchPropertyCnt;
409                 }
410                 ++propertyCnt;
411             }
412
413             StringBuilder newPathParams = new StringBuilder(
414                 (pathParams == null ? "" : pathParams.toString()) + sbPathParameters.toString());
415             String useName;
416             for (int k = 0; addTypeV != null && k < addTypeV.size(); ++k) {
417                 String addType = addTypeV.elementAt(k);
418                 namespaceFilter.add(getXmlRootElementName(addType));
419                 logger.debug("addType: " + addType);
420
421                 if (opId == null || !opId.contains(addType)) {
422                     processJavaTypeElementSwagger(addType, getJavaTypeElementSwagger(addType),
423                         pathSb, definitionsSb, path, tag == null ? useTag : tag, useOpId, null,
424                         newPathParams, validEdges);
425                 }
426                 // need item name of array
427                 String itemName = processJavaTypeElementSwagger(addType,
428                     getJavaTypeElementSwagger(addType), pathSb, definitionsSb, path,
429                     tag == null ? useTag : tag, useOpId, "array", null, null);
430
431                 if (itemName != null) {
432                     if (addType.equals("AaiInternal")) {
433                         logger.debug("addType AaiInternal, skip properties");
434
435                     } else if (getItemName == null) {
436                         ++propertyCnt;
437                         sbProperties.append("      ").append(getXmlRootElementName(addType))
438                             .append(":\n");
439                         if ("RelationshipList".equals(addType)) {
440                             sbProperties.append("        type: object\n");
441                             sbProperties.append("        $ref: \"#/definitions/").append(itemName)
442                                 .append("\"\n");
443                             sbPropertiesPatch.append("      ")
444                                 .append(getXmlRootElementName(addType)).append(":\n");
445                             sbPropertiesPatch.append("        type: object\n");
446                             sbPropertiesPatch.append("        $ref: \"#/definitions/")
447                                 .append(itemName).append("\"\n");
448                             ++patchPropertyCnt;
449                         } else {
450                             if ("relationship".equals(itemName)) {
451                                 System.out.println(
452                                     v + "-relationship added as array for getItemName null");
453                             }
454                             sbProperties.append("        type: array\n        items:\n");
455                             sbProperties.append("          $ref: \"#/definitions/")
456                                 .append("".equals(itemName) ? "inventory-item-data" : itemName)
457                                 .append("\"\n");
458                         }
459                         if (StringUtils.isNotEmpty(elementDescription)) {
460                             sbProperties.append("        description: ").append(elementDescription)
461                                 .append("\n");
462                         }
463                     }
464                 } else {
465                     if (("java.util.ArrayList")
466                         .equals(xmlElementElement.getAttribute("container-type"))) {
467                         // need properties for getXmlRootElementName(addType)
468                         namespaceFilter.add(getXmlRootElementName(addType));
469                         newPathParams = new StringBuilder(
470                             (pathParams == null ? "" : pathParams.toString()) + sbParameters);
471                         processJavaTypeElementSwagger(addType, getJavaTypeElementSwagger(addType),
472                             pathSb, definitionsSb, path, tag == null ? useTag : tag, useOpId, null,
473                             newPathParams, validEdges);
474                         useName = getXmlRootElementName(addType);
475                         sbProperties.append("      ").append(useName).append(":\n");
476                         if ("relationship".equals(useName)) {
477                             sbProperties.append("        type: object\n");
478                             sbProperties.append("        $ref: \"#/definitions/relationship\"\n");
479                             sbPropertiesPatch.append("        type: object\n");
480                             sbPropertiesPatch
481                                 .append("        $ref: \"#/definitions/relationship\"\n");
482                             ++patchPropertyCnt;
483                         } else {
484                             sbProperties.append("        type: array\n        items:          \n");
485                             sbProperties.append("          $ref: \"#/definitions/"
486                                 + getXmlRootElementName(addType) + "\"\n");
487                             if (StringUtils.isNotEmpty(elementDescription)) {
488                                 sbProperties.append("        description: ")
489                                     .append(elementDescription).append("\n");
490                             }
491                         }
492
493                     } else {
494                         // Make sure certain types added to the filter don't appear
495                         if (!nodeFilter.contains(getXmlRootElementName(addType))) {
496                             sbProperties.append("      ").append(getXmlRootElementName(addType))
497                                 .append(":\n");
498                             sbProperties.append("        type: object\n");
499                             sbProperties.append("        $ref: \"#/definitions/")
500                                 .append(getXmlRootElementName(addType)).append("\"\n");
501                         }
502                     }
503                     if (StringUtils.isNotEmpty(elementDescription)) {
504                         sbProperties.append("        description: ").append(elementDescription)
505                             .append("\n");
506                     }
507                     ++propertyCnt;
508                 }
509             }
510         }
511
512         if (sbParameters.toString().length() > 0) {
513             if (pathParams == null) {
514                 pathParams = new StringBuilder();
515             }
516             pathParams.append(sbPathParameters);
517         }
518         GetOperation get = new GetOperation(useOpId, xmlRootElementName, tag, path,
519             pathParams == null ? "" : pathParams.toString());
520         pathSb.append(get);
521         logger.debug("opId vs useOpId:" + opId + " vs " + useOpId + " PathParams=" + pathParams);
522         // add PUT
523         PutOperation put = new PutOperation(useOpId, xmlRootElementName, tag, path,
524             pathParams == null ? "" : pathParams.toString(), this.v, this.basePath);
525         pathSb.append(put);
526         // add PATCH
527         PatchOperation patch = new PatchOperation(useOpId, xmlRootElementName, tag, path,
528             pathParams == null ? "" : pathParams.toString(), this.v, this.basePath);
529         patch.setPrefixForPatchRef(patchDefinePrefix);
530         pathSb.append(patch);
531         // add DELETE
532         DeleteOperation del = new DeleteOperation(useOpId, xmlRootElementName, tag, path,
533             pathParams == null ? "" : pathParams.toString());
534         pathSb.append(del);
535         if (generatedJavaType.containsKey(xmlRootElementName)) {
536             logger.debug("xmlRootElementName(1)=" + xmlRootElementName);
537             return null;
538         }
539
540         boolean processingInventoryDef = false;
541         String dict = null;
542         if (xmlRootElementName.equals("inventory")) {
543             // inventory properties for each oxm to be concatenated
544             processingInventoryDef = true;
545             if (inventoryDefSb == null) {
546                 inventoryDefSb = new StringBuilder();
547                 definitionsSb.append("  ").append(xmlRootElementName).append(":\n");
548                 definitionsLocalSb.append("  ").append(xmlRootElementName).append(":\n");
549                 definitionsLocalSb.append("    properties:\n");
550             }
551         } else if (xmlRootElementName.equals("relationship")) {
552             definitionsSb.append("  " + "relationship-dict" + ":\n");
553             definitionsLocalSb.append("  " + "relationship-dict" + ":\n");
554             dict = getDictionary(xmlRootElementName);
555         } else {
556             definitionsSb.append("  ").append(xmlRootElementName).append(":\n");
557             definitionsLocalSb.append("  ").append(xmlRootElementName).append(":\n");
558         }
559         // Collection<EdgeDescription> edges = edgeRuleSet.getEdgeRules(xmlRootElementName );
560         DeleteFootnoteSet footnotes = new DeleteFootnoteSet(xmlRootElementName);
561         StringBuilder sbEdge = new StringBuilder();
562         LinkedHashSet<String> preventDelete = new LinkedHashSet<String>();
563         String prevent = null;
564         String nodeCaption = "      ###### Related Nodes\n";
565         try {
566             EdgeRuleQuery q =
567                 new EdgeRuleQuery.Builder(xmlRootElementName).version(v).fromOnly().build();
568             Multimap<String, EdgeRule> results = ei.getRules(q);
569             SortedSet<String> ss = new TreeSet<String>(results.keySet());
570             sbEdge.append(nodeCaption);
571             nodeCaption = "";
572             for (String key : ss) {
573                 results.get(key).stream()
574                     .filter((i) -> (i.getFrom().equals(xmlRootElementName) && (!i.isPrivateEdge())))
575                     .forEach((i) -> {
576                         logger.info(new String(new StringBuilder("      - TO ").append(i.getTo())
577                             .append(i.getDirection().toString()).append(i.getContains())));
578                     });
579                 results.get(key).stream()
580                     .filter((i) -> (i.getFrom().equals(xmlRootElementName) && (!i.isPrivateEdge())))
581                     .forEach((i) -> {
582                         sbEdge.append("      - TO ").append(i.getTo());
583                         EdgeDescription ed = new EdgeDescription(i);
584                         String footnote = ed.getAlsoDeleteFootnote(xmlRootElementName);
585                         sbEdge.append(ed.getRelationshipDescription("TO", xmlRootElementName))
586                             .append(footnote).append("\n");
587                         if (StringUtils.isNotEmpty(footnote)) {
588                             footnotes.add(footnote);
589                         }
590                     });
591                 results.get(key).stream()
592                     .filter((i) -> (i.getFrom().equals(xmlRootElementName)
593                         && (!i.isPrivateEdge() && i.getPreventDelete().equals("OUT"))))
594                     .forEach((i) -> {
595                         preventDelete.add(i.getTo().toUpperCase());
596                     });
597             }
598         } catch (Exception e) {
599             logger.debug("xmlRootElementName: " + xmlRootElementName + " from edge exception\n", e);
600         }
601         try {
602             EdgeRuleQuery q1 =
603                 new EdgeRuleQuery.Builder(xmlRootElementName).version(v).toOnly().build();
604             Multimap<String, EdgeRule> results = ei.getRules(q1);
605             SortedSet<String> ss = new TreeSet<String>(results.keySet());
606             sbEdge.append(nodeCaption);
607             for (String key : ss) {
608                 results.get(key).stream()
609                     .filter((i) -> (i.getTo().equals(xmlRootElementName) && (!i.isPrivateEdge())))
610                     .forEach((i) -> {
611                         sbEdge.append("      - FROM ").append(i.getFrom());
612                         EdgeDescription ed = new EdgeDescription(i);
613                         String footnote = ed.getAlsoDeleteFootnote(xmlRootElementName);
614                         sbEdge.append(ed.getRelationshipDescription("FROM", xmlRootElementName))
615                             .append(footnote).append("\n");
616                         if (StringUtils.isNotEmpty(footnote)) {
617                             footnotes.add(footnote);
618                         }
619                     });
620                 results.get(key).stream()
621                     .filter((i) -> (i.getTo().equals(xmlRootElementName) && (!i.isPrivateEdge())))
622                     .forEach((i) -> {
623                         logger
624                             .info(new String(new StringBuilder("      - FROM ").append(i.getFrom())
625                                 .append(i.getDirection().toString()).append(i.getContains())));
626                     });
627                 results.get(key).stream()
628                     .filter((i) -> (i.getTo().equals(xmlRootElementName)
629                         && (!i.isPrivateEdge() && i.getPreventDelete().equals("IN"))))
630                     .forEach((i) -> {
631                         preventDelete.add(i.getFrom().toUpperCase());
632                     });
633             }
634         } catch (Exception e) {
635             logger.debug("xmlRootElementName: " + xmlRootElementName + " to edge exception\n", e);
636         }
637         if (preventDelete.size() > 0) {
638             prevent = xmlRootElementName.toUpperCase() + " cannot be deleted if related to "
639                 + String.join(",", preventDelete);
640             logger.debug(prevent);
641         }
642
643         if (StringUtils.isNotEmpty(prevent)) {
644             footnotes.add(prevent);
645         }
646         if (footnotes.footnotes.size() > 0) {
647             sbEdge.append(footnotes.toString());
648         }
649         validEdges = sbEdge.toString();
650
651         // Handle description property. Might have a description OR valid edges OR both OR neither.
652         // Only put a description: tag if there is at least one.
653         if (StringUtils.isNotEmpty(pathDescriptionProperty) || StringUtils.isNotEmpty(validEdges)) {
654             definitionsSb.append("    description: |\n");
655             definitionsLocalSb.append("    description: |\n");
656
657             if (pathDescriptionProperty != null) {
658                 definitionsSb.append("      ").append(pathDescriptionProperty).append("\n");
659                 definitionsLocalSb.append("      ").append(pathDescriptionProperty).append("\n");
660             }
661             if (StringUtils.isNotEmpty(validEdges)) {
662                 definitionsSb.append(validEdges);
663                 definitionsLocalSb.append(validEdges);
664             }
665         }
666         if (patchPropertyCnt > 0) {
667             definitionsLocalPatchSb.append("  " + patchDefinePrefix).append(xmlRootElementName)
668                 .append(":\n");
669             if (StringUtils.isNotEmpty(pathDescriptionProperty)
670                 || StringUtils.isNotEmpty(validEdges)) {
671                 definitionsLocalPatchSb.append("    description: |\n");
672             }
673             if (pathDescriptionProperty != null) {
674                 definitionsLocalPatchSb.append("      ").append(pathDescriptionProperty)
675                     .append("\n");
676             }
677             if (StringUtils.isNotEmpty(validEdges)) {
678                 definitionsLocalPatchSb.append(validEdges);
679             }
680             definitionsLocalPatchSb.append("    properties:\n");
681         }
682
683         if (requiredCnt > 0) {
684             definitionsSb.append(sbRequired);
685             definitionsLocalSb.append(sbRequired);
686         }
687
688         if (propertyCnt > 0) {
689             definitionsSb.append("    properties:\n");
690             definitionsSb.append(sbProperties);
691             if (!processingInventoryDef) {
692                 definitionsLocalSb.append("    properties:\n");
693             }
694             definitionsLocalSb.append(sbProperties);
695             definitionsLocalPatchSb.append(sbPropertiesPatch);
696         }
697         try {
698             namespaceFilter.add(xmlRootElementName);
699             if (xmlRootElementName.equals("inventory")) {
700                 // will add to javaTypeDefinitions at end
701                 inventoryDefSb.append(definitionsLocalSb.toString());
702             } else if (xmlRootElementName.equals("relationship")) {
703                 javaTypeDefinitions.put(xmlRootElementName, dict);
704                 javaTypeDefinitions.put(xmlRootElementName + "-dict",
705                     definitionsLocalSb.toString());
706             } else {
707                 javaTypeDefinitions.put(xmlRootElementName, definitionsLocalSb.toString());
708                 if (!"relationship-list".equals(xmlRootElementName)) {
709                     javaTypeDefinitions.put(patchDefinePrefix + xmlRootElementName,
710                         definitionsLocalPatchSb.toString());
711                 }
712             }
713         } catch (Exception e) {
714             logger.error("Exception adding in javaTypeDefinitions", e);
715         }
716         if (xmlRootElementName.equals("inventory")) {
717             logger.trace("skip xmlRootElementName(2)=" + xmlRootElementName);
718             return null;
719         }
720         generatedJavaType.put(xmlRootElementName, null);
721         /*
722          * if( validTag(javaTypeName) && javaTypeName == useTag && tag == null) {
723          * String nameSpaceResult =
724          * getDocumentHeader()+pathSb.toString()+appendDefinitions(namespaceFilter);
725          * writeYAMLfile(javaTypeName, nameSpaceResult);
726          * totalPathSbAccumulator.append(pathSb);
727          * pathSb.delete(0, pathSb.length());
728          * namespaceFilter.clear();
729          * }
730          */
731         logger.trace("xmlRootElementName(2)=" + xmlRootElementName);
732         return null;
733     }
734
735     private void writeYAMLfile(String outfileName, String fileContent) {
736         outfileName = (StringUtils.isEmpty(outfileName)) ? "aai_swagger" : outfileName;
737         outfileName = (outfileName.lastIndexOf(File.separator) == -1)
738             ? yaml_dir + File.separator + outfileName + "_" + v.toString() + "." + generateTypeYAML
739             : outfileName;
740         File outfile = new File(outfileName);
741         File parentDir = outfile.getParentFile();
742         if (parentDir != null && !parentDir.exists()) {
743             parentDir.mkdirs();
744         }
745         try {
746             if (!outfile.createNewFile()) {
747                 logger.error("File {} already exist", outfileName);
748             }
749         } catch (IOException e) {
750             logger.error("Exception creating output file " + outfileName, e);
751         }
752         try {
753             Charset charset = StandardCharsets.UTF_8;
754             Path path = Paths.get(outfileName);
755             try (BufferedWriter bw = Files.newBufferedWriter(path, charset)) {
756                 bw.write(fileContent);
757             }
758         } catch (IOException e) {
759             logger.error("Exception writing output file " + outfileName, e);
760         }
761     }
762
763     public boolean validTag(String tag) {
764         if (tag != null) {
765             // set ignore to true to skip Actions and Search
766             return checkTopLevel(tag, true);
767         }
768         return false;
769     }
770
771 }