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