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