From 68b67795514c2e8dbd095fc238d19c7d03d8c621 Mon Sep 17 00:00:00 2001 From: Ali Ustek Date: Wed, 15 Jan 2020 11:56:20 +0000 Subject: [PATCH] Issue #340: Use AsciiDoc AST to create open v3 support (#381) * Use Asciidoc AST to create OpenApi v3 support * Add `Overview` information * Add `Servers` information * Fix document authors * Fix document authors * Add `Servers` information * Add `Servers` information * Add `Servers` information * Add `Servers` information * Add `Path Parameters` information * Add `Path Parameters` information * Add `Path Responses Links` information * Add `Tags` information * Add `Comonents.Schemas` information * Add `Comonents.Parameters` information * Add `External Docs` information * Add `Components Responses` information * Add `Components Headers` information * Add `Components Links` information * Delete unused test fixtures * Change asciidoctorJ dependency to asciidoctorj-api, to remove the the need for JRuby --- libraries.gradle | 4 +- swagger2markup-asciidoc/build.gradle | 3 +- .../{converter => }/AsciidocConverter.java | 353 ++-- .../swagger2markup/adoc/ast/DocumentImpl.java | 612 ------- .../adoc/ast/impl/BlockImpl.java | 70 + .../adoc/ast/impl/CellImpl.java | 112 ++ .../adoc/ast/impl/ColumnImpl.java | 81 + .../adoc/ast/impl/ContentNodeImpl.java | 309 ++++ .../adoc/ast/impl/CursorImpl.java | 36 + .../ast/impl/DescriptionListEntryImpl.java | 66 + .../adoc/ast/impl/DescriptionListImpl.java | 72 + .../adoc/ast/impl/DocumentImpl.java | 89 + .../adoc/ast/impl/ListImpl.java | 56 + .../adoc/ast/impl/ListItemImpl.java | 63 + .../adoc/ast/impl/ParagraphBlockImpl.java | 38 + .../adoc/ast/impl/PhraseNodeImpl.java | 50 + .../swagger2markup/adoc/ast/impl/RowImpl.java | 21 + .../adoc/ast/impl/SectionImpl.java | 142 ++ .../adoc/ast/impl/StructuralNodeImpl.java | 179 ++ .../adoc/ast/impl/TableImpl.java | 306 ++++ .../adoc/ast/impl/TitleImpl.java | 34 + .../converter/AsciiDocConverterRegistry.java | 11 - .../internal/DelimitedBlockNode.java | 7 +- .../adoc/converter/internal/Delimiters.java | 8 +- .../adoc/converter/internal/SourceNode.java | 4 +- ...ctor.jruby.converter.spi.ConverterRegistry | 2 +- .../AsciidocConverterTest.java | 5 +- .../expected/arrows-and-boxes-example.ad | 1 + .../asciidoc/expected/brokeninclude.asciidoc | 1 + .../asciidoc/expected/changeattribute.adoc | 1 + .../asciidoc/expected/chronicles-example.adoc | 184 +-- .../expected/document-with-arrays.adoc | 17 +- .../resources/asciidoc/expected/simple.adoc | 77 + .../asciidoc/original/chronicles-example.adoc | 2 +- .../resources/asciidoc/original/simple.adoc | 28 + .../src/test/resources/logback.xml | 14 + .../test/resources/yaml/swagger_petstore.yaml | 111 -- .../src/test/resources/logback.xml | 4 +- swagger2markup-swagger-v2/build.gradle | 1 - swagger2markup-swagger-v3/build.gradle | 7 +- .../swagger2markup/OpenApi2AsciiDoc.java | 19 + .../OpenApiComponentsSection.java | 62 + .../github/swagger2markup/OpenApiHelpers.java | 313 ++++ .../swagger2markup/OpenApiInfoSection.java | 93 ++ .../swagger2markup/OpenApiPathsSection.java | 37 + .../swagger2markup/OpenApiServerSection.java | 52 + .../swagger2markup/OpenApiTagsSection.java | 41 + .../swagger2markup/OpenApiV3Converter.java | 14 - .../swagger2markup/OpenApi2AsciiDocTest.java | 51 + .../OpenApiV3ConverterTest.java | 12 - .../src/test/resources/asciidoc/petstore.adoc | 1456 +++++++++++++++++ .../src/test/resources/asciidoc/simple.adoc | 270 +++ .../src/test/resources/logback.xml | 14 + .../src/test/resources/open_api/petstore.yaml | 709 ++++++++ .../src/test/resources/open_api/simple.yaml | 115 ++ 55 files changed, 5343 insertions(+), 1096 deletions(-) rename swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/{converter => }/AsciidocConverter.java (73%) delete mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/DocumentImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/BlockImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/CellImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ColumnImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ContentNodeImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/CursorImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/DescriptionListEntryImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/DescriptionListImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/DocumentImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ListImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ListItemImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ParagraphBlockImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/PhraseNodeImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/RowImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/SectionImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/StructuralNodeImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/TableImpl.java create mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/TitleImpl.java delete mode 100644 swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/AsciiDocConverterRegistry.java rename swagger2markup-asciidoc/src/test/java/io/github/swagger2markup/adoc/{converter => }/AsciidocConverterTest.java (92%) create mode 100644 swagger2markup-asciidoc/src/test/resources/asciidoc/expected/simple.adoc create mode 100644 swagger2markup-asciidoc/src/test/resources/asciidoc/original/simple.adoc create mode 100644 swagger2markup-asciidoc/src/test/resources/logback.xml delete mode 100644 swagger2markup-asciidoc/src/test/resources/yaml/swagger_petstore.yaml create mode 100644 swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApi2AsciiDoc.java create mode 100644 swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiComponentsSection.java create mode 100644 swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiHelpers.java create mode 100644 swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiInfoSection.java create mode 100644 swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiPathsSection.java create mode 100644 swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiServerSection.java create mode 100644 swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiTagsSection.java delete mode 100644 swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiV3Converter.java create mode 100644 swagger2markup-swagger-v3/src/test/java/io/github/swagger2markup/OpenApi2AsciiDocTest.java delete mode 100644 swagger2markup-swagger-v3/src/test/java/io/github/swagger2markup/OpenApiV3ConverterTest.java create mode 100644 swagger2markup-swagger-v3/src/test/resources/asciidoc/petstore.adoc create mode 100644 swagger2markup-swagger-v3/src/test/resources/asciidoc/simple.adoc create mode 100644 swagger2markup-swagger-v3/src/test/resources/logback.xml create mode 100644 swagger2markup-swagger-v3/src/test/resources/open_api/petstore.yaml create mode 100644 swagger2markup-swagger-v3/src/test/resources/open_api/simple.yaml diff --git a/libraries.gradle b/libraries.gradle index 9ba47327..41f04255 100644 --- a/libraries.gradle +++ b/libraries.gradle @@ -6,7 +6,7 @@ ext { dependencyOverrides = [:] } implLibraries = [ - asciiDocJ : "org.asciidoctor:asciidoctorj:2.1.0", + asciiDocJApi : "org.asciidoctor:asciidoctorj-api:2.2.0", commonsBeanUtils : "commons-beanutils:commons-beanutils:1.9.4", commonsCodec : "commons-codec:commons-codec:1.13", commonsCollections4: "org.apache.commons:commons-collections4:4.4", @@ -27,7 +27,7 @@ implLibraries = [ ] testLibraries = [ - asciiDocJ : "org.asciidoctor:asciidoctorj:2.1.0", + asciiDocJ : "org.asciidoctor:asciidoctorj:2.2.0", assertj : "org.assertj:assertj-core:3.13.2", assertjDiff: "io.github.robwin:assertj-diff:0.1.1", junit : "junit:junit:4.12", diff --git a/swagger2markup-asciidoc/build.gradle b/swagger2markup-asciidoc/build.gradle index d448ec6b..b69f0d0c 100644 --- a/swagger2markup-asciidoc/build.gradle +++ b/swagger2markup-asciidoc/build.gradle @@ -15,10 +15,11 @@ dependencies { // resolutionStrategy.force dependencyOverrides.findBugs // resolutionStrategy.force dependencyOverrides.jaksonCore } - implementation implLibraries.asciiDocJ + implementation implLibraries.asciiDocJApi implementation implLibraries.commonsText implementation implLibraries.slf4j testImplementation implLibraries.commonsIO + testImplementation testLibraries.asciiDocJ testImplementation testLibraries.junit testImplementation testLibraries.logback } diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/AsciidocConverter.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/AsciidocConverter.java similarity index 73% rename from swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/AsciidocConverter.java rename to swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/AsciidocConverter.java index 107112e6..43f45789 100644 --- a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/AsciidocConverter.java +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/AsciidocConverter.java @@ -1,4 +1,4 @@ -package io.github.swagger2markup.adoc.converter; +package io.github.swagger2markup.adoc; import io.github.swagger2markup.adoc.converter.internal.*; import org.apache.commons.lang3.StringUtils; @@ -14,6 +14,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.LongStream; import java.util.stream.Stream; import static io.github.swagger2markup.adoc.converter.internal.Delimiters.*; @@ -28,7 +29,6 @@ public class AsciidocConverter extends StringConverter { private final Pattern emptyLineOrStartWith = Pattern.compile("(?m)^\\s*(?:\\r?\\n)|(?m)^\\s+"); private final Pattern coListItemIdPattern = Pattern.compile(".*-(\\d+)"); private final Pattern tableColumnsStylePattern = Pattern.compile("((\\d+)\\*)?([<^>])?(\\.[<^>])?(\\d+)?([adehlmsv])?"); - private final Pattern cellStylePattern = Pattern.compile("((\\d+)[+*])?(\\.(\\d+)\\+)?([<^>])?(\\.[<^>])?([adehlmsv])?"); private static final java.util.List attributeToExclude = Arrays.asList( "localtime", @@ -106,6 +106,7 @@ public String convert(ContentNode node, String transform, Map op case "colist": return convertCoList((List) node); case "embedded": + case "document": return convertEmbedded((Document) node); case "example": return convertExample((Block) node); @@ -149,8 +150,6 @@ public String convert(ContentNode node, String transform, Map op return convertVerse((StructuralNode) node); case "video": return convertVideo(node); - case "document": - return convertDocument(node); case "toc": return convertToc(node); case "pass": @@ -162,14 +161,73 @@ public String convert(ContentNode node, String transform, Map op return convertList((List) node); case "list_item": return convertListItem((ListItem) node); - case "descriptionListEntry": - return convertDescriptionListEntry((DescriptionListEntry) node, true); default: - logger.debug("Don't know how to convert: " + transform); + logger.debug("Don't know how to convert transform: [" + transform + "] Node: " + node); return null; } } + String convertEmbedded(Document node) { + logger.debug("convertEmbedded"); + StringBuilder sb = new StringBuilder(); + appendId(node, sb); + if (StringUtils.isNotBlank(node.getDoctitle())) { + sb.append(repeat(node.getLevel() + 1,DOCUMENT_TITLE)).append(' ').append(StringEscapeUtils.unescapeHtml4(node.getDoctitle())).append(LINE_SEPARATOR); + } + Map attributes = node.getAttributes(); + appendAuthors(sb, attributes); + appendRevisionDetails(sb, attributes); + appendDocumentAttributes(sb, attributes); + appendTrailingNewLine(sb); + appendChildBlocks(node, sb); + return sb.toString(); + } + + private void appendAuthors(StringBuilder sb, Map attributes) { + Long authorCount = (Long) attributes.getOrDefault("authorcount", 0L); + if (authorCount == 1) { + String author = getAuthorDetail(attributes, "author", "email"); + if (StringUtils.isNotBlank(author)) { + sb.append(author).append(LINE_SEPARATOR); + } + } else if (authorCount > 1) { + String authors = LongStream.rangeClosed(1, authorCount) + .mapToObj(i -> getAuthorDetail(attributes, "author_" + i, "email_" + i)) + .collect(Collectors.joining("; ")); + + if (StringUtils.isNotBlank(authors)) { + sb.append(authors).append(LINE_SEPARATOR); + } + } + } + + private void appendDocumentAttributes(StringBuilder sb, Map attributes) { + attributes.forEach((k, v) -> { + if (!attributeToExclude.contains(k) && v != null && !v.toString().isEmpty()) + sb.append(COLON).append(k).append(COLON).append(" ").append(v).append(LINE_SEPARATOR); + }); + } + + private void appendRevisionDetails(StringBuilder sb, Map attributes) { + String revDetails = Stream.of(attributes.get("revnumber"), attributes.get("revdate")).filter(Objects::nonNull) + .filter(o -> !o.toString().isEmpty()).map(Object::toString) + .collect(Collectors.joining(", ")); + + if (!revDetails.isEmpty()) { + sb.append("v").append(revDetails).append(LINE_SEPARATOR); + } + } + + private String getAuthorDetail(Map attributes, String authorKey, String emailKey) { + String author = attributes.getOrDefault(authorKey, "").toString(); + String email = attributes.getOrDefault(emailKey, "").toString(); + if (StringUtils.isNotBlank(email)) { + email = " <" + email + ">"; + } + + return (author + email).trim(); + } + private String convertInlineAnchor(PhraseNode node) { logger.debug("convertInlineAnchor"); String type = node.getType(); @@ -293,28 +351,28 @@ private String convertExample(Block node) { } private String convertInlineButton(ContentNode node) { - logger.debug("convertInlineButton"); + logger.debug("convertInlineButton: name" + node.getNodeName()); return "convertInlineButton"; } private String convertInlineCallout(ContentNode node) { - logger.debug("convertInlineCallout"); + logger.debug("convertInlineCallout: name" + node.getNodeName()); return "convertInlineCallout"; } private String convertInlineBreak(ContentNode node) { - logger.debug("convertInlineBreak"); + logger.debug("convertInlineBreak: name" + node.getNodeName()); return "convertInlineBreak"; } private String convertInlineFootnote(ContentNode node) { - logger.debug("convertInlineFootnote"); + logger.debug("convertInlineFootnote: name" + node.getNodeName()); return "convertInlineFootnote"; } private String convertInlineImage(PhraseNode node) { logger.debug("convertInlineImage"); - if(node.getType().equals("icon")){ + if (node.getType().equals("icon")) { return (new IconNode(node)).toAsciiDocContent(); } else { return (new BlockImageNode(node)).toAsciiDocContent(); @@ -322,17 +380,17 @@ private String convertInlineImage(PhraseNode node) { } private String convertInlineIndexTerm(ContentNode node) { - logger.debug("convertInlineIndexTerm"); + logger.debug("convertInlineIndexTerm: name" + node.getNodeName()); return "convertInlineIndexTerm"; } private String convertInlineKbd(ContentNode node) { - logger.debug("convertInlineKbd"); + logger.debug("convertInlineKbd: name" + node.getNodeName()); return "convertInlineKbd"; } private String convertInlineMenu(ContentNode node) { - logger.debug("convertInlineMenu"); + logger.debug("convertInlineMenu: name" + node.getNodeName()); return "convertInlineMenu"; } @@ -357,7 +415,7 @@ private String convertOpen(StructuralNode node) { } private String convertPageBreak(ContentNode node) { - logger.debug("convertPageBreak"); + logger.debug("convertPageBreak: name" + node.getNodeName()); return DELIMITER_PAGE_BREAK + LINE_SEPARATOR; } @@ -394,12 +452,12 @@ private String convertSidebar(StructuralNode node) { } private String convertStem(ContentNode node) { - logger.debug("convertStem"); + logger.debug("convertStem: name" + node.getNodeName()); return "convertStem"; } private String convertThematicBreak(ContentNode node) { - logger.debug("convertThematicBreak"); + logger.debug("convertThematicBreak: name" + node.getNodeName()); return DELIMITER_THEMATIC_BREAK + LINE_SEPARATOR; } @@ -424,57 +482,45 @@ private String convertVerse(StructuralNode node) { if (matches) { sb.append(LINE_SEPARATOR).append(DELIMITER_VERSE); } - sb.append(LINE_SEPARATOR); + appendTrailingNewLine(sb); return sb.toString(); } private String convertVideo(ContentNode node) { - logger.debug("convertVideo"); + logger.debug("convertVideo: name" + node.getNodeName()); return "convertVideo"; } - private String convertDocument(ContentNode node) { - logger.debug("convertDocument"); - return "convertDocument"; - } - private String convertToc(ContentNode node) { - logger.debug("convertToc"); + logger.debug("convertToc: name" + node.getNodeName()); return "convertToc"; } private String convertPass(ContentNode node) { - logger.debug("convertPass"); + logger.debug("convertPass: name" + node.getNodeName()); return "convertPass"; } private String convertAudio(ContentNode node) { - logger.debug("convertAudio"); + logger.debug("convertAudio: name" + node.getNodeName()); return "convertAudio"; } - private String convertPhraseNode(PhraseNode node) { - logger.debug("convertPhraseNode"); - StringBuilder sb = new StringBuilder(); - String target = node.getTarget(); - if (node.getType().equals("link")) { - if (StringUtils.startsWithAny(target, supportedUrlSchemes)) { - sb.append(target).append(ATTRIBUTES_BEGIN).append(node.getReftext()).append(ATTRIBUTES_END); - } else { - sb.append("include::").append(node.getText()).append(ATTRIBUTES_BEGIN).append(ATTRIBUTES_END).append(LINE_SEPARATOR); - } - } else { - logger.debug("Dont know type: " + node.getType()); - } - return sb.toString(); - } - private String convertCell(Cell node) { logger.debug("convertCell"); - return node.getSource(); + StringBuilder sb = new StringBuilder(); + String source = node.getSource(); + if (StringUtils.isNotBlank(source)) { + sb.append(source); + } + Document innerDocument = node.getInnerDocument(); + if (null != innerDocument) { + appendChildBlocks(innerDocument, sb, false); + } + return sb.toString().replaceAll(LINE_SEPARATOR + LINE_SEPARATOR + "+", LINE_SEPARATOR + LINE_SEPARATOR); } - private String convertRow(Row node, java.util.List columnStyles) { + private String convertRow(Row node, java.util.List columnStyles, String delimiterTableCell) { logger.debug("convertRow"); StringBuilder sb = new StringBuilder(); node.getCells().forEach(cell -> { @@ -495,7 +541,7 @@ private String convertRow(Row node, java.util.List columnStyles) boolean hAlignmentAdded = false; TableCellHorizontalAlignment hAlignment = TableCellHorizontalAlignment.fromName(cell.getHorizontalAlignment().name()); if ((null != hAlignment) && (null == tableCellStyle || hAlignment != tableCellStyle.horizontalAlignment)) { - hAlignmentAdded = true; + hAlignmentAdded = true; addNewLine = true; sb.append(hAlignment.getDelimiter()); } @@ -511,7 +557,7 @@ private String convertRow(Row node, java.util.List columnStyles) addNewLine = true; sb.append(style.getShortHand()); } - sb.append(DELIMITER_CELL).append(convertCell(cell)); + sb.append(delimiterTableCell).append(convertCell(cell)); if (addNewLine) { sb.append(LINE_SEPARATOR); } else { @@ -555,84 +601,100 @@ private String convertTable(Table node) { StringBuilder sb = new StringBuilder(); appendTitle(node, sb); sb.append(new TableNode(node).toAsciiDocContent()); - sb.append(DELIMITER_TABLE).append(LINE_SEPARATOR); - appendRows(node.getHeader(), sb, columnStyles); - appendRows(node.getBody(), sb, columnStyles); - appendRows(node.getFooter(), sb, columnStyles); - sb.append(DELIMITER_TABLE).append(LINE_SEPARATOR); + boolean innerTable = isInnerTable(node); + String tableDelimiter = innerTable ? DELIMITER_INNER_TABLE : DELIMITER_TABLE; + String cellDelimiter = innerTable ? DELIMITER_INNER_TABLE_CELL : DELIMITER_TABLE_CELL; + sb.append(tableDelimiter).append(LINE_SEPARATOR); + appendRows(node.getHeader(), sb, columnStyles, cellDelimiter); + appendRows(node.getBody(), sb, columnStyles, cellDelimiter); + appendRows(node.getFooter(), sb, columnStyles, cellDelimiter); + sb.append(tableDelimiter).append(LINE_SEPARATOR); return sb.toString(); } - private void appendRows(java.util.List rows, StringBuilder sb, java.util.List columnStyles) { - rows.forEach(row -> { - sb.append(convertRow(row, columnStyles)).append(LINE_SEPARATOR); - }); + private boolean isInnerTable(ContentNode node) { + if(null != node) { + ContentNode parent = node.getParent(); + if (null != parent) { + return parent instanceof Table || isInnerTable(parent); + } + } + return false; + } + + private void appendRows(java.util.List rows, StringBuilder sb, java.util.List columnStyles, String delimiterTableCell) { + rows.forEach(row -> sb.append(convertRow(row, columnStyles, delimiterTableCell)).append(LINE_SEPARATOR)); } private String convertDescriptionList(DescriptionList node) { logger.debug("convertDescriptionList"); - StringBuilder result = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - appendTitle(node, result); + appendTitle(node, sb); String style = Optional.ofNullable(node.getStyle()).orElse(""); switch (style) { case STYLE_HORIZONTAL: - result.append(ATTRIBUTES_BEGIN).append(STYLE_HORIZONTAL).append(ATTRIBUTES_END).append(LINE_SEPARATOR); - node.getItems().forEach(item -> result.append(convertDescriptionListEntry(item, false)).append(LINE_SEPARATOR)); + sb.append(ATTRIBUTES_BEGIN).append(STYLE_HORIZONTAL).append(ATTRIBUTES_END).append(LINE_SEPARATOR); + node.getItems().forEach(item -> sb.append(convertDescriptionListEntry(item, node.getLevel(), false))); break; case STYLE_Q_AND_A: - result.append(ATTRIBUTES_BEGIN).append(STYLE_Q_AND_A).append(ATTRIBUTES_END).append(LINE_SEPARATOR); + sb.append(ATTRIBUTES_BEGIN).append(STYLE_Q_AND_A).append(ATTRIBUTES_END).append(LINE_SEPARATOR); default: - node.getItems().forEach(item -> result.append(convertDescriptionListEntry(item, true)).append(LINE_SEPARATOR)); + node.getItems().forEach(item -> sb.append(convertDescriptionListEntry(item, node.getLevel(), true))); break; } - result.append(LINE_SEPARATOR); + appendTrailingNewLine(sb); - return result.toString(); + return sb.toString(); } - private String convertDescriptionListEntry(DescriptionListEntry node, Boolean descriptionOnNewLine) { + private String convertDescriptionListEntry(DescriptionListEntry node, int level, Boolean descriptionOnNewLine) { logger.debug("convertDescriptionListEntry"); - StringBuilder result = new StringBuilder(); - node.getTerms().forEach(term -> result.append(Optional.ofNullable(term.getSource()).orElse("")).append(MARKER_D_LIST_ITEM).append(LINE_SEPARATOR)); + StringBuilder sb = new StringBuilder(); + String delimiter = repeat(level + 1, MARKER_D_LIST_ITEM); + String entryTerms = node.getTerms().stream() + .map(term -> Optional.ofNullable(term.getSource()).orElse("")) + .collect(Collectors.joining(delimiter + LINE_SEPARATOR, "", delimiter)); + sb.append(entryTerms); ListItem description = node.getDescription(); if (null != description) { if (descriptionOnNewLine) { - result.append(LINE_SEPARATOR); + sb.append(LINE_SEPARATOR); + } + String desc = Optional.ofNullable(description.getSource()).orElse(""); + if (StringUtils.isNotBlank(desc)) { + sb.append(desc).append(LINE_SEPARATOR); } - result.append(Optional.ofNullable(description.getSource()).orElse("")); - appendChildBlocks(description, result); + appendChildBlocks(description, sb); } - return result.toString(); + return sb.toString(); } private String convertListing(Block node) { logger.debug("convertListing"); StringBuilder sb = new StringBuilder(); appendTitle(node, sb); - switch (node.getStyle()) { - case STYLE_SOURCE: - sb.append(new SourceNode(node).toAsciiDocContent()); - break; - default: - sb.append(new BlockListingNode(node).toAsciiDocContent()); + if (STYLE_SOURCE.equals(node.getStyle())) { + sb.append(new SourceNode(node).toAsciiDocContent()); + } else { + sb.append(new BlockListingNode(node).toAsciiDocContent()); } return sb.toString(); } private String convertUList(List node) { logger.debug("convertUList"); - StringBuilder result = new StringBuilder(); - appendStyle(node, result); - appendTitle(node, result); - appendChildBlocks(node, result); - result.append(LINE_SEPARATOR); - return result.toString(); + StringBuilder sb = new StringBuilder(); + appendStyle(node, sb); + appendTitle(node, sb); + appendChildBlocks(node, sb); + appendTrailingNewLine(sb); + return sb.toString(); } private String convertOList(List node) { logger.debug("convertOList"); - StringBuilder result = new StringBuilder(); + StringBuilder sb = new StringBuilder(); java.util.List attrs = new ArrayList<>(); String start = node.getAttribute("start", "").toString(); if (StringUtils.isNotBlank(start)) { @@ -642,12 +704,12 @@ private String convertOList(List node) { attrs.add("%reversed"); } if (!attrs.isEmpty()) { - result.append(ATTRIBUTES_BEGIN).append(String.join(",", attrs)).append(ATTRIBUTES_END).append(LINE_SEPARATOR); + sb.append(ATTRIBUTES_BEGIN).append(String.join(",", attrs)).append(ATTRIBUTES_END).append(LINE_SEPARATOR); } - appendTitle(node, result); - appendChildBlocks(node, result); - result.append(LINE_SEPARATOR); - return result.toString(); + appendTitle(node, sb); + appendChildBlocks(node, sb); + appendTrailingNewLine(sb); + return sb.toString(); } private String convertCoList(List node) { @@ -659,7 +721,7 @@ private String convertCoList(List node) { private String convertListItem(ListItem node) { logger.debug("convertListItem"); - StringBuilder result = new StringBuilder(); + StringBuilder sb = new StringBuilder(); String marker = Optional.ofNullable(node.getMarker()).orElse(repeat(node.getLevel(), MARKER_LIST_ITEM)); @@ -669,22 +731,22 @@ private String convertListItem(ListItem node) { marker = marker.replaceAll("\\d+", matcher.group(1)); } - result.append(marker).append(" "); + sb.append(marker).append(" "); if (node.hasAttribute("checkbox")) { - result.append('['); + sb.append('['); if (node.hasAttribute("checked")) { - result.append('x'); + sb.append('x'); } else { - result.append(' '); + sb.append(' '); } - result.append(']').append(' '); + sb.append(']').append(' '); } - result.append(Optional.ofNullable(node.getSource()).orElse("")); - result.append(LINE_SEPARATOR); - appendChildBlocks(node, result); - return result.toString(); + sb.append(Optional.ofNullable(node.getSource()).orElse("")); + appendTrailingNewLine(sb); + appendChildBlocks(node, sb); + return sb.toString(); } private String convertList(List node) { @@ -709,60 +771,29 @@ private String convertImage(StructuralNode node) { private String convertLiteral(StructuralNode node) { logger.debug("convertLiteral"); return ATTRIBUTES_BEGIN + node.getContext() + ATTRIBUTES_END + LINE_SEPARATOR + - unescapeContent(node.getContent().toString()) + LINE_SEPARATOR; + StringEscapeUtils.unescapeHtml4(node.getContent().toString()) + LINE_SEPARATOR; } private String convertParagraph(StructuralNode node) { logger.debug("convertParagraph"); - return new ParagraphAttributes(node).toAsciiDocContent() + - ((Block) node).getSource() + LINE_SEPARATOR; + StringBuilder sb = new StringBuilder(); + sb.append(new ParagraphAttributes(node).toAsciiDocContent()); + appendSource((Block) node, sb); + appendTrailingNewLine(sb); + return sb.toString(); } private String convertSection(Section node) { logger.debug("convertSection"); StringBuilder sb = new StringBuilder(); + appendId(node, sb); sb.append(new DelimitedBlockNode(node).toAsciiDocContent()).append(StringUtils.repeat(TITLE, node.getLevel() + 1)) - .append(" ").append(unescapeContent(node.getTitle())).append(LINE_SEPARATOR).append(LINE_SEPARATOR); + .append(" ").append(StringEscapeUtils.unescapeHtml4(node.getTitle())).append(LINE_SEPARATOR); appendChildBlocks(node, sb); - + appendTrailingNewLine(sb); return sb.toString(); } - private String convertEmbedded(Document node) { - logger.debug("convertEmbedded"); - StringBuilder sb = new StringBuilder(); - - sb.append(DOCUMENT_TITLE).append(unescapeContent(node.getDoctitle())); - Map attributes = node.getAttributes(); - String authors = attributes.getOrDefault("authors", "").toString(); - if (!authors.isEmpty()) { - sb.append(LINE_SEPARATOR).append(authors).append(LINE_SEPARATOR); - } - - String revDetails = Stream.of(attributes.get("revnumber"), attributes.get("revdate")).filter(Objects::nonNull) - .filter(o -> !o.toString().isEmpty()).map(Object::toString) - .collect(Collectors.joining(", ")); - - if (!revDetails.isEmpty()) { - sb.append("v").append(revDetails).append(LINE_SEPARATOR); - } - - attributes.forEach((k, v) -> { - if (!attributeToExclude.contains(k) && v != null && !v.toString().isEmpty()) - sb.append(LINE_SEPARATOR).append(COLON).append(k).append(COLON).append(" ").append(v); - }); - String content = node.getContent().toString(); - if (StringUtils.isNotBlank(content)) { - sb.append(LINE_SEPARATOR).append(LINE_SEPARATOR).append(content); - } - return sb.toString(); - } - - private String unescapeContent(String content) { - return StringEscapeUtils.unescapeHtml4(content); - } - - private void append_link_constraint_attrs(ContentNode node, java.util.List attrs) { String rel = node.getAttribute("nofollow-option").toString(); String window = node.getAttributes().get("window").toString(); @@ -784,14 +815,50 @@ private String repeat(int count, String with) { return new String(new char[count]).replace("\0", with); } - private void appendChildBlocks(StructuralNode node, StringBuilder sb) { - node.getBlocks().forEach(block -> sb.append(block.convert()).append(LINE_SEPARATOR)); + private void appendChildBlocks(StructuralNode parentNode, StringBuilder sb) { + appendChildBlocks(parentNode, sb, true); + } + + private void appendChildBlocks(StructuralNode parentNode, StringBuilder sb, boolean addTrailingLineSeparator) { + final boolean isParentAListItem = parentNode instanceof ListItem || parentNode instanceof DescriptionListEntry; + parentNode.getBlocks().forEach(childNode -> { + String childNodeValue = childNode.convert(); + if (StringUtils.isNotBlank(childNodeValue)) { + if (isParentAListItem && (sb.toString().contains("+" + LINE_SEPARATOR) || !(childNode instanceof List || childNode instanceof DescriptionList))) { + sb.append('+').append(LINE_SEPARATOR); + } + sb.append(childNodeValue); + if (addTrailingLineSeparator && !StringUtils.endsWith(childNodeValue, LINE_SEPARATOR)) { + sb.append(LINE_SEPARATOR); + } + } + }); + } + + private void appendTrailingNewLine(StringBuilder sb) { + if (!sb.toString().endsWith(LINE_SEPARATOR + LINE_SEPARATOR)) { + sb.append(LINE_SEPARATOR); + } + } + + private void appendId(StructuralNode node, StringBuilder sb) { + String id = node.getId(); + if (StringUtils.isNotBlank(id)) { + sb.append("[[").append(id).append("]]").append(LINE_SEPARATOR); + } + } + + private void appendSource(Block node, StringBuilder sb) { + String source = node.getSource(); + if (StringUtils.isNotBlank(source)) { + sb.append(source).append(LINE_SEPARATOR); + } } private void appendTitle(StructuralNode node, StringBuilder sb) { String title = node.getTitle(); if (StringUtils.isNotBlank(title)) { - sb.append(".").append(unescapeContent(title)).append(LINE_SEPARATOR); + sb.append(".").append(StringEscapeUtils.unescapeHtml4(title)).append(LINE_SEPARATOR); } } diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/DocumentImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/DocumentImpl.java deleted file mode 100644 index 678bc2ca..00000000 --- a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/DocumentImpl.java +++ /dev/null @@ -1,612 +0,0 @@ -package io.github.swagger2markup.adoc.ast; - -import org.asciidoctor.ast.*; - -import java.util.List; -import java.util.Map; - -public class DocumentImpl implements Document { - /** - * @return The Title structure for this document. - * @see Title - */ - @Override - public Title getStructuredDoctitle() { - return null; - } - - /** - * @return The title as a String. - * @see Title - */ - @Override - public String getDoctitle() { - return null; - } - - /** - * @return The title as a String. - * @see Title - * @deprecated Please use {@link #getDoctitle()} - */ - @Override - public String doctitle() { - return null; - } - - /** - * @param backend - * @return basebackend attribute value - */ - @Override - public boolean isBasebackend(String backend) { - return false; - } - - /** - * @param backend - * @return basebackend attribute value - * @deprecated Please use {@link #isBasebackend(String)} - */ - @Override - public boolean basebackend(String backend) { - return false; - } - - /** - * @return options defined in document. - */ - @Override - public Map getOptions() { - return null; - } - - /** - * Gets the current counter with the given name and increases its value. - * At the first invocation the counter will return 1. - * After the call the value of the counter is set to the returned value plus 1. - * - * @param name - * @return - */ - @Override - public int getAndIncrementCounter(String name) { - return 0; - } - - /** - * Gets the current counter with the given name and increases its value. - * At the first invocation the counter will return the given initial value. - * After the call the value of the counter is set to the returned value plus 1. - * - * @param name - * @param initialValue - * @return - */ - @Override - public int getAndIncrementCounter(String name, int initialValue) { - return 0; - } - - /** - * @return Whether the sourcemap is enabled. - */ - @Override - public boolean isSourcemap() { - return false; - } - - /** - * Toggles the sourcemap option. - *

- * This method must be called before the document is parsed, such as - * from a Preprocessor extension. Otherwise, it has no effect. - * - * @param state The state in which to put the sourcemap (true = on, false = off). - */ - @Override - public void setSourcemap(boolean state) { - - } - - /** - * @deprecated Please use {@linkplain #getTitle()} instead - */ - @Override - public String title() { - return null; - } - - @Override - public String getTitle() { - return null; - } - - @Override - public void setTitle(String title) { - - } - - @Override - public String getCaption() { - return null; - } - - @Override - public void setCaption(String caption) { - - } - - /** - * @deprecated Please use {@linkplain #getStyle()} instead - */ - @Override - public String style() { - return null; - } - - @Override - public String getStyle() { - return null; - } - - @Override - public void setStyle(String style) { - - } - - /** - * @return The list of child blocks of this block - * @deprecated Please use {@linkplain #getBlocks()} instead - */ - @Override - public List blocks() { - return null; - } - - /** - * @return The list of child blocks of this block - */ - @Override - public List getBlocks() { - return null; - } - - /** - * Appends a new child block as the last block to this block. - * - * @param block The new child block added as last child to this block. - */ - @Override - public void append(StructuralNode block) { - - } - - /** - * @deprecated Please use {@linkplain #getContent()} instead - */ - @Override - public Object content() { - return null; - } - - @Override - public Object getContent() { - return null; - } - - @Override - public String convert() { - return null; - } - - @Override - public List findBy(Map selector) { - return null; - } - - @Override - public int getLevel() { - return 0; - } - - /** - * Returns the content model. - * - * @return the content model - * @see ContentModel - */ - @Override - public String getContentModel() { - return null; - } - - /** - * Returns the source location of this block. - * This information is only available if the {@code sourcemap} option is enabled when loading or rendering the document. - * - * @return the source location of this block or {@code null} if the {@code sourcemap} option is not enabled when loading the document. - */ - @Override - public Cursor getSourceLocation() { - return null; - } - - /** - * Returns the list of enabled substitutions. - * - * @return A list of substitutions enabled for this node, e.g. ["specialcharacters", "quotes", "attributes", "replacements", "macros", "post_replacements"] for paragraphs. - * @see Asciidoctor User Manual - */ - @Override - public List getSubstitutions() { - return null; - } - - /** - * @param substitution the name of a substitution, e.g. {@link #SUBSTITUTION_POST_REPLACEMENTS} - * @return true if the name of the given substitution is enabled. - * @see Asciidoctor User Manual - */ - @Override - public boolean isSubstitutionEnabled(String substitution) { - return false; - } - - /** - * Removes the given substitution from this node. - * - * @param substitution the name of a substitution, e.g. {@link #SUBSTITUTION_QUOTES} - * @see Asciidoctor User Manual - */ - @Override - public void removeSubstitution(String substitution) { - - } - - /** - * Adds the given substitution to this node at the end of the substitution list. - * - * @param substitution the name of a substitution, e.g. {@link #SUBSTITUTION_MACROS} - * @see Asciidoctor User Manual - */ - @Override - public void addSubstitution(String substitution) { - - } - - /** - * Adds the given substitution to this node at the beginning of the substitution list. - * - * @param substitution the name of a substitution, e.g. {@link #SUBSTITUTION_ATTRIBUTES} - * @see Asciidoctor User Manual - */ - @Override - public void prependSubstitution(String substitution) { - - } - - /** - * Sets the given substitutions on this node overwriting all other substitutions. - * - * @param substitution the name of a substitution, e.g. {@link #SUBSTITUTION_SPECIAL_CHARACTERS} - * @see Asciidoctor User Manual - */ - @Override - public void setSubstitutions(String... substitution) { - - } - - /** - * @return A unique ID for this node - * @deprecated Please use {@link #getId()} - */ - @Override - public String id() { - return null; - } - - /** - * @return A unique ID for this node - */ - @Override - public String getId() { - return null; - } - - @Override - public void setId(String id) { - - } - - @Override - public String getNodeName() { - return null; - } - - /** - * @deprecated Use {@linkplain #getParent()} instead. - */ - @Override - public ContentNode parent() { - return null; - } - - @Override - public ContentNode getParent() { - return null; - } - - /** - * @deprecated Use {@linkplain #getContext()} instead. - */ - @Override - public String context() { - return null; - } - - @Override - public String getContext() { - return null; - } - - /** - * @deprecated Use {@linkplain #getDocument()} instead. - */ - @Override - public Document document() { - return null; - } - - @Override - public Document getDocument() { - return null; - } - - @Override - public boolean isInline() { - return false; - } - - @Override - public boolean isBlock() { - return false; - } - - @Override - public Map getAttributes() { - return null; - } - - /** - * @param name - * @param defaultValue - * @param inherit - * @return - * @deprecated Use {@link #getAttribute(Object, Object, boolean)} instead - */ - @Override - public Object getAttr(Object name, Object defaultValue, boolean inherit) { - return null; - } - - /** - * @param name - * @param defaultValue - * @return - * @deprecated Use {@link #getAttribute(Object, Object)} instead - */ - @Override - public Object getAttr(Object name, Object defaultValue) { - return null; - } - - /** - * @param name - * @return - * @deprecated Use {@link #getAttribute(Object)} instead - */ - @Override - public Object getAttr(Object name) { - return null; - } - - @Override - public Object getAttribute(Object name, Object defaultValue, boolean inherit) { - return null; - } - - @Override - public Object getAttribute(Object name, Object defaultValue) { - return null; - } - - @Override - public Object getAttribute(Object name) { - return null; - } - - /** - * @param name - * @return {@code true} if this node or the document has an attribute with the given name - * @deprecated Use {@link #hasAttribute(Object)} instead - */ - @Override - public boolean hasAttr(Object name) { - return false; - } - - /** - * @param name - * @param inherited - * @return {@code true} if the current node or depending on the inherited parameter the document has an attribute with the given name. - * @deprecated Use {@link #hasAttribute(Object, boolean)} instead - */ - @Override - public boolean hasAttr(Object name, boolean inherited) { - return false; - } - - /** - * @param name - * @return {@code true} if this node or the document has an attribute with the given name - */ - @Override - public boolean hasAttribute(Object name) { - return false; - } - - /** - * @param name - * @param inherited - * @return {@code true} if the current node or depending on the inherited parameter the document has an attribute with the given name. - */ - @Override - public boolean hasAttribute(Object name, boolean inherited) { - return false; - } - - /** - * @param name - * @param expected - * @return - * @deprecated Use {@link #isAttribute(Object, Object)} instead. - */ - @Override - public boolean isAttr(Object name, Object expected) { - return false; - } - - /** - * @param name - * @param expected - * @param inherit - * @return - * @deprecated Use {@link #isAttribute(Object, Object, boolean)} instead. - */ - @Override - public boolean isAttr(Object name, Object expected, boolean inherit) { - return false; - } - - /** - * @param name - * @param expected - * @return - */ - @Override - public boolean isAttribute(Object name, Object expected) { - return false; - } - - /** - * @param name - * @param expected - * @param inherit - * @return - */ - @Override - public boolean isAttribute(Object name, Object expected, boolean inherit) { - return false; - } - - /** - * @param name - * @param value - * @param overwrite - * @return - * @deprecated Use {@link #setAttribute(Object, Object, boolean)} instead. - */ - @Override - public boolean setAttr(Object name, Object value, boolean overwrite) { - return false; - } - - @Override - public boolean setAttribute(Object name, Object value, boolean overwrite) { - return false; - } - - @Override - public boolean isOption(Object name) { - return false; - } - - @Override - public boolean isRole() { - return false; - } - - @Override - public boolean hasRole(String role) { - return false; - } - - @Override - public String getRole() { - return null; - } - - /** - * @deprecated Use {@linkplain #getRole()} instead. - */ - @Override - public String role() { - return null; - } - - @Override - public List getRoles() { - return null; - } - - @Override - public void addRole(String role) { - - } - - @Override - public void removeRole(String role) { - - } - - @Override - public boolean isReftext() { - return false; - } - - @Override - public String getReftext() { - return null; - } - - @Override - public String iconUri(String name) { - return null; - } - - @Override - public String mediaUri(String target) { - return null; - } - - @Override - public String imageUri(String targetImage) { - return null; - } - - @Override - public String imageUri(String targetImage, String assetDirKey) { - return null; - } - - @Override - public String readAsset(String path, Map opts) { - return null; - } - - @Override - public String normalizeWebPath(String path, String start, boolean preserveUriTarget) { - return null; - } -} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/BlockImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/BlockImpl.java new file mode 100644 index 00000000..a798653b --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/BlockImpl.java @@ -0,0 +1,70 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.asciidoctor.ast.Block; +import org.asciidoctor.ast.StructuralNode; + +import java.util.*; + +public class BlockImpl extends StructuralNodeImpl implements Block { + + private List lines; + + public BlockImpl(StructuralNode parent, String context) { + this(parent, context, ""); + } + + public BlockImpl(StructuralNode parent, String context, Object content) { + this(parent, context, new HashMap<>(), content); + } + + public BlockImpl(StructuralNode parent, String context, Map attributes) { + this(parent, context, attributes, ""); + } + + public BlockImpl(StructuralNode parent, String context, Map attributes, Object content) { + this(parent, context, attributes, new ArrayList<>(), content, new ArrayList<>(), "", new ArrayList<>()); + } + + public BlockImpl(StructuralNode parent, String context, Map attributes, List roles, + Object content, List blocks, String contentModel, List subs) { + this(parent, context, attributes, roles, content, blocks, calculateLevel(parent), contentModel, subs); + } + + public BlockImpl(StructuralNode parent, String context, Map attributes, List roles, + Object content, List blocks, Integer level, String contentModel, List subs) { + super(parent, context, attributes, roles, content, blocks, level, contentModel, subs); + this.lines = new ArrayList<>(); + } + + @Override + @Deprecated + public List lines() { + return getLines(); + } + + @Override + public List getLines() { + return lines; + } + + @Override + public void setLines(List lines) { + this.lines = lines; + } + + @Override + @Deprecated + public String source() { + return getSource(); + } + + @Override + public String getSource() { + return String.join("\n", lines); + } + + @Override + public void setSource(String source) { + setLines(Arrays.asList(source.split("\n"))); + } +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/CellImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/CellImpl.java new file mode 100644 index 00000000..677f5fc7 --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/CellImpl.java @@ -0,0 +1,112 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.asciidoctor.ast.Cell; +import org.asciidoctor.ast.Column; +import org.asciidoctor.ast.Document; +import org.asciidoctor.ast.Table; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CellImpl extends ContentNodeImpl implements Cell { + + private final int colspan; + private final int rowspan; + private String text; + private String style; + private Document innerDocument; + + public CellImpl(Column parent, String text) { + this(parent, "table_cell", new HashMap<>(), new ArrayList<>(), 0, 0); + this.text = text; + } + + public CellImpl(Column parent, Document innerDocument) { + this(parent, "table_cell", new HashMap<>(), new ArrayList<>(), 0, 0); + this.innerDocument = innerDocument; + } + + public CellImpl(Column parent, String context, Map attributes, List roles, int colspan, int rowspan) { + super(parent, context, attributes, roles); + this.colspan = colspan; + this.rowspan = rowspan; + } + + @Override + public Column getColumn() { + return (Column) getParent(); + } + + @Override + public int getColspan() { + return colspan; + } + + @Override + public int getRowspan() { + return rowspan; + } + + @Override + public String getText() { + return text; + } + + @Override + public String getSource() { + return text; + } + + @Override + public void setSource(String source) { + this.text = source; + } + + @Override + public Object getContent() { + return text; + } + + @Override + public String getStyle() { + return style; + } + + @Override + public void setStyle(String style) { + this.style = style; + } + + @Override + public Table.HorizontalAlignment getHorizontalAlignment() { + return Table.HorizontalAlignment.valueOf(((String) getAttribute("halign", "left")).toUpperCase()); + } + + @Override + public void setHorizontalAlignment(Table.HorizontalAlignment halign) { + setAttribute("halign", halign.name().toLowerCase(), true); + } + + @Override + public Table.VerticalAlignment getVerticalAlignment() { + return Table.VerticalAlignment.valueOf(((String) getAttribute("valign", "top")).toUpperCase()); + } + + @Override + public void setVerticalAlignment(Table.VerticalAlignment valign) { + setAttribute("valign", valign.name().toLowerCase(), true); + } + + @Override + public Document getInnerDocument() { + return innerDocument; + } + + @Override + public void setInnerDocument(Document document) { + this.innerDocument = document; + } + +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ColumnImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ColumnImpl.java new file mode 100644 index 00000000..b3c139b5 --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ColumnImpl.java @@ -0,0 +1,81 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.asciidoctor.ast.Column; +import org.asciidoctor.ast.Table; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ColumnImpl extends ContentNodeImpl implements Column { + + private String style; + private Number columnNumber = -1; + private Number width = 0; + + public ColumnImpl(Table parent) { + this(parent, "table_column", new HashMap<>(), new ArrayList<>()); + } + + public ColumnImpl(Table parent, String context, Map attributes, List roles) { + super(parent, context, attributes, roles); + } + + @Override + public String getStyle() { + return style; + } + + @Override + public void setStyle(String style) { + this.style = style; + } + + @Override + public Table getTable() { + return (Table) getParent(); + } + + @Override + public int getColumnNumber() { + return columnNumber.intValue(); + } + + public void setColumnNumber(Integer columnNumber) { + setAttribute("colnumber", columnNumber, true); + this.columnNumber = columnNumber; + } + + @Override + public int getWidth() { + return width.intValue(); + } + + @Override + public void setWidth(int width) { + setAttribute("width", width, true); + this.width = width; + } + + @Override + public Table.HorizontalAlignment getHorizontalAlignment() { + return Table.HorizontalAlignment.valueOf(((String) getAttribute("halign", "left")).toUpperCase()); + } + + @Override + public void setHorizontalAlignment(Table.HorizontalAlignment halign) { + setAttribute("halign", halign.name().toLowerCase(), true); + } + + @Override + public Table.VerticalAlignment getVerticalAlignment() { + return Table.VerticalAlignment.valueOf(((String) getAttribute("valign", "top")).toUpperCase()); + } + + @Override + public void setVerticalAlignment(Table.VerticalAlignment valign) { + setAttribute("valign", valign.name().toLowerCase(), true); + } + +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ContentNodeImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ContentNodeImpl.java new file mode 100644 index 00000000..5716776f --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ContentNodeImpl.java @@ -0,0 +1,309 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.Document; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@SuppressWarnings("SuspiciousMethodCalls") +public abstract class ContentNodeImpl implements ContentNode { + + private String id; + private final String context; + private final Map attributes; + private final List roles; + private final ContentNode parent; + + public ContentNodeImpl(ContentNode parent, String context) { + this(parent, context, new HashMap<>(), new ArrayList<>()); + } + + public ContentNodeImpl(ContentNode parent, String context, Map attributes, List roles) { + this.parent = parent; + this.context = context; + this.attributes = attributes; + this.roles = roles; + } + + @Override + @Deprecated + public String id() { + return getId(); + } + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id.toLowerCase().replaceAll("\\s+", "_"); + } + + @Override + @Deprecated + public String context() { + return getContext(); + } + + @Override + public String getContext() { + return context; + } + + @Override + @Deprecated + public ContentNode parent() { + return getParent(); + } + + @Override + public ContentNode getParent() { + return parent; + } + + @Override + @Deprecated + public Document document() { + return getDocument(); + } + + @Override + public Document getDocument() { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + @Override + public String getNodeName() { + return getContext(); + } + + @Override + public boolean isInline() { + return false; + } + + @Override + public boolean isBlock() { + return false; + } + + @Override + public Map getAttributes() { + return attributes; + } + + @Override + @Deprecated + public Object getAttr(Object name, Object defaultValue, boolean inherit) { + return getAttribute(name, defaultValue, inherit); + } + + @Override + @Deprecated + public Object getAttr(Object name, Object defaultValue) { + return getAttribute(name, defaultValue); + } + + @Override + @Deprecated + public Object getAttr(Object name) { + return getAttribute(name); + } + + @Override + public Object getAttribute(Object name, Object defaultValue, boolean inherit) { + return getAttribute(name, defaultValue); + } + + @Override + public Object getAttribute(Object name, Object defaultValue) { + return attributes.getOrDefault(name, defaultValue); + } + + @Override + public Object getAttribute(Object name) { + return attributes.get(name); + } + + @Override + @Deprecated + public boolean isAttr(Object name, Object expected, boolean inherit) { + return isAttribute(name, expected, inherit); + } + + @Override + @Deprecated + public boolean isAttr(Object name, Object expected) { + return isAttribute(name, expected); + } + + @Override + public boolean isAttribute(Object name, Object expected, boolean inherit) { + return isAttribute(name, expected); + } + + @Override + public boolean isAttribute(Object name, Object expected) { + try { + if (attributes.containsKey(name)) { + return attributes.get(name).equals(expected); + } else return false; + } catch (Exception e) { + return false; + } + } + + @Override + @Deprecated + public boolean hasAttr(Object name) { + return hasAttribute(name); + } + + @Override + @Deprecated + public boolean hasAttr(Object name, boolean inherited) { + return hasAttribute(name, inherited); + } + + @Override + public boolean hasAttribute(Object name) { + return attributes.containsKey(name); + } + + @Override + public boolean hasAttribute(Object name, boolean inherited) { + return hasAttribute(name); + } + + @Override + @Deprecated + public boolean setAttr(Object name, Object value, boolean overwrite) { + return setAttribute(name, value, overwrite); + } + + @Override + public boolean setAttribute(Object name, Object value, boolean overwrite) { + return setAttribute((String)name, value, overwrite); + } + + public boolean setAttribute(String name, Object value, boolean overwrite) { + try { + if (overwrite) { + attributes.put(name, value); + } else { + attributes.putIfAbsent(name, value); + } + return true; + } catch (Exception e) { + return false; + } + } + + public Object removeAttribute(String name){ + return attributes.remove(name); + } + + public boolean removeAttribute(String name, Object value){ + return attributes.remove(name, value); + } + + @Override + public boolean isOption(Object name) { + try { + Object o = attributes.get(name + "-option"); + return null != o && o.toString().equals(""); + }catch (Exception ignored){ + return false; + } + } + + public boolean setOption(String name){ + return setAttribute(name + "-option", "", true); + } + + public Object removeOption(String name){ + return removeAttribute(name + "-option"); + } + + @Override + public boolean isRole() { + return false; + } + + @Override + public String getRole() { + return String.join(",", roles); + } + + @Override + @Deprecated + public String role() { + return getRole(); + } + + @Override + public List getRoles() { + return roles; + } + + @Override + public boolean hasRole(String role) { + return roles.contains(role); + } + + @Override + public void addRole(String role) { + roles.add(role); + } + + @Override + public void removeRole(String role) { + roles.remove(role); + } + + @Override + public boolean isReftext() { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + @Override + public String getReftext() { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + @Override + public String iconUri(String name) { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + @Override + public String mediaUri(String target) { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + @Override + public String imageUri(String targetImage) { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + @Override + public String imageUri(String targetImage, String assetDirKey) { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + @Override + public String readAsset(String path, Map opts) { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + @Override + public String normalizeWebPath(String path, String start, boolean preserveUriTarget) { + throw new UnsupportedOperationException("Not implemented, yet"); + } + +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/CursorImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/CursorImpl.java new file mode 100644 index 00000000..3bae8dab --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/CursorImpl.java @@ -0,0 +1,36 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.asciidoctor.ast.Cursor; + +public class CursorImpl implements Cursor { + + private int lineno; + + public CursorImpl() { + } + + @Override + public int getLineNumber() { + return lineno; + } + + @Override + public String getPath() { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + @Override + public String getDir() { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + @Override + public String getFile() { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + @Override + public String toString() { + throw new UnsupportedOperationException("Not implemented, yet"); + } +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/DescriptionListEntryImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/DescriptionListEntryImpl.java new file mode 100644 index 00000000..a896609c --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/DescriptionListEntryImpl.java @@ -0,0 +1,66 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.asciidoctor.ast.DescriptionListEntry; +import org.asciidoctor.ast.ListItem; +import org.asciidoctor.ast.StructuralNode; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class DescriptionListEntryImpl extends StructuralNodeImpl implements DescriptionListEntry { + + private final List terms; + private ListItem description; + + public DescriptionListEntryImpl(StructuralNode parent) { + this(parent, new ArrayList<>()); + } + + public DescriptionListEntryImpl(StructuralNode parent, List terms) { + this(parent, terms, null); + } + + public DescriptionListEntryImpl(StructuralNode parent, List terms, ListItem description) { + this(parent, null, terms, description); + } + + public DescriptionListEntryImpl(StructuralNode parent, Object content, List terms, ListItem description) { + this(parent, new HashMap<>(), new ArrayList<>(), content, new ArrayList<>(), "", new ArrayList<>(), terms, description); + } + + public DescriptionListEntryImpl(StructuralNode parent, Map attributes, List roles, + Object content, List blocks, String contentModel, List subs, + List terms, ListItem description) { + this(parent, attributes, roles, content, blocks, null != parent ? parent.getLevel() : 1, contentModel, subs, terms, description); + } + + public DescriptionListEntryImpl(StructuralNode parent, Map attributes, List roles, + Object content, List blocks, Integer level, String contentModel, + List subs, List terms, ListItem description) { + super(parent, "dlist_item", attributes, roles, content, blocks, level, contentModel, subs); + this.terms = terms; + this.description = description; + } + + @Override + public List getTerms() { + return terms; + } + + public boolean addTerm(ListItem term) { + return terms.add(term); + } + + @Override + public ListItem getDescription() { + return description; + } + + public void setDescription(final ListItem description) { + this.description = description; + } + + +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/DescriptionListImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/DescriptionListImpl.java new file mode 100644 index 00000000..39eeedcb --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/DescriptionListImpl.java @@ -0,0 +1,72 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.asciidoctor.ast.DescriptionList; +import org.asciidoctor.ast.DescriptionListEntry; +import org.asciidoctor.ast.StructuralNode; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class DescriptionListImpl extends StructuralNodeImpl implements DescriptionList { + + public static final String CONTEXT = "dlist"; + private List items; + + public DescriptionListImpl(StructuralNode parent) { + this(parent, new ArrayList<>()); + } + + public DescriptionListImpl(StructuralNode parent, List items) { + this(parent, null, items); + } + + public DescriptionListImpl(StructuralNode parent, Object content, List items) { + this(parent, new HashMap<>(), new ArrayList<>(), content, new ArrayList<>(), "", new ArrayList<>(), items); + } + + public DescriptionListImpl(StructuralNode parent, Map attributes, List roles, + Object content, List blocks, String contentModel, + List subs, List items) { + this(parent, attributes, roles, content, blocks, calculateLevel(parent), contentModel, subs, items); + } + + public DescriptionListImpl(StructuralNode parent, Map attributes, List roles, + Object content, List blocks, Integer level, + String contentModel, List subs, List items) { + super(parent, CONTEXT, attributes, roles, content, blocks, level, contentModel, subs); + this.items = items; + } + + @Override + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + + public void addEntry(DescriptionListEntry entry) { + this.items.add(entry); + } + + @Override + public boolean hasItems() { + return !items.isEmpty(); + } + + @Override + @Deprecated + public String render() { + return convert(); + } + + protected static Integer calculateLevel(StructuralNode parent) { + int level = 1; + if (parent instanceof DescriptionList) + level = parent.getLevel() + 1; + return level; + } +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/DocumentImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/DocumentImpl.java new file mode 100644 index 00000000..47db1bfa --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/DocumentImpl.java @@ -0,0 +1,89 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.asciidoctor.ast.Document; +import org.asciidoctor.ast.StructuralNode; +import org.asciidoctor.ast.Title; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class DocumentImpl extends StructuralNodeImpl implements Document { + + public DocumentImpl() { + this(null); + } + + public DocumentImpl(StructuralNode parent) { + this(parent, "document", ""); + } + + public DocumentImpl(StructuralNode parent, String context, Object content) { + this(parent, context, new HashMap<>(), new ArrayList<>(), content, new ArrayList<>(), "", new ArrayList<>()); + } + + public DocumentImpl(StructuralNode parent, String context, Map attributes, List roles, + Object content, List blocks, String contentModel, + List subs) { + this(parent, context, attributes, roles, content, blocks, null != parent ? parent.getLevel() + 1 : 0, contentModel, subs); + } + + public DocumentImpl(StructuralNode parent, String context, Map attributes, List roles, + Object content, List blocks, Integer level, String contentModel, + List subs) { + super(parent, context, attributes, roles, content, blocks, level, contentModel, subs); + } + + @Override + public boolean isBasebackend(String backend) { + return isAttribute("basebackend", backend); + } + + @Override + @Deprecated + public boolean basebackend(String backend) { + return isBasebackend(backend); + } + + @Override + public Map getOptions() { + return null; + } + + @Override + public Title getStructuredDoctitle() { + return (Title) getOptions().get("doctitle"); + } + + @Override + public String getDoctitle() { + return getTitle(); + } + + @Override + @Deprecated + public String doctitle() { + return getDoctitle(); + } + + @Override + public int getAndIncrementCounter(String name) { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + @Override + public int getAndIncrementCounter(String name, int initialValue) { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + @Override + public boolean isSourcemap() { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + @Override + public void setSourcemap(boolean state) { + throw new UnsupportedOperationException("Not implemented, yet"); + } +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ListImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ListImpl.java new file mode 100644 index 00000000..b029bb19 --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ListImpl.java @@ -0,0 +1,56 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.asciidoctor.ast.List; +import org.asciidoctor.ast.StructuralNode; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +public class ListImpl extends StructuralNodeImpl implements List { + + private final java.util.List items; + + public ListImpl(StructuralNode parent, String context) { + this(parent, context, new ArrayList<>()); + } + + + public ListImpl(StructuralNode parent, String context, java.util.List items) { + this(parent, context, null, items); + } + + public ListImpl(StructuralNode parent, String context, Object content, java.util.List items) { + this(parent, context, new HashMap<>(), new ArrayList<>(), content, new ArrayList<>(), "", new ArrayList<>(), items); + } + + public ListImpl(StructuralNode parent, String context, Map attributes, java.util.List roles, + Object content, java.util.List blocks, + String contentModel, java.util.List subs, java.util.List items) { + this(parent, context, attributes, roles, content, blocks, null != parent ? parent.getLevel() + 1 : 0, contentModel, subs, items); + } + + public ListImpl(StructuralNode parent, String context, Map attributes, java.util.List roles, + Object content, java.util.List blocks, + Integer level, String contentModel, java.util.List subs, java.util.List items) { + super(parent, context, attributes, roles, content, blocks, level, contentModel, subs); + this.items = items; + } + + @Override + public java.util.List getItems() { + return items; + } + + @Override + public boolean hasItems() { + return !items.isEmpty(); + } + + @Override + @Deprecated + public String render() { + return convert(); + } + +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ListItemImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ListItemImpl.java new file mode 100644 index 00000000..bbc3d60d --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ListItemImpl.java @@ -0,0 +1,63 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.apache.commons.lang3.StringUtils; +import org.asciidoctor.ast.ListItem; +import org.asciidoctor.ast.StructuralNode; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ListItemImpl extends StructuralNodeImpl implements ListItem { + + private final String marker; + private String text; + + public ListItemImpl(StructuralNode parent, String text) { + this(parent, "list_item", null, "*", text); + } + + public ListItemImpl(StructuralNode parent, String context, Object content, String marker, String text) { + this(parent, context, new HashMap<>(), new ArrayList<>(), content, new ArrayList<>(), "", new ArrayList<>(), marker, text); + } + + public ListItemImpl(StructuralNode parent, String context, Map attributes, List roles, + Object content, List blocks, String contentModel, + List subs, String marker, String text) { + this(parent, context, attributes, roles, content, blocks, null != parent ? parent.getLevel() : 1, contentModel, subs, marker, text); + } + + public ListItemImpl(StructuralNode parent, String context, Map attributes, List roles, + Object content, List blocks, Integer level, + String contentModel, List subs, String marker, String text) { + super(parent, context, attributes, roles, content, blocks, level, contentModel, subs); + this.marker = marker; + this.text = text; + } + + @Override + public String getMarker() { + return marker; + } + + @Override + public String getText() { + return text; + } + + @Override + public String getSource() { + return text; + } + + @Override + public void setSource(String source) { + this.text = source; + } + + @Override + public boolean hasText() { + return StringUtils.isNotBlank(text); + } +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ParagraphBlockImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ParagraphBlockImpl.java new file mode 100644 index 00000000..8037a86d --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/ParagraphBlockImpl.java @@ -0,0 +1,38 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.asciidoctor.ast.StructuralNode; + +import java.util.List; +import java.util.Map; + +public class ParagraphBlockImpl extends BlockImpl { + + public static final String CONTEXT = "paragraph"; + + public ParagraphBlockImpl(StructuralNode parent) { + super(parent, CONTEXT); + } + + public ParagraphBlockImpl(StructuralNode parent, Object content) { + super(parent, CONTEXT, content); + } + + public ParagraphBlockImpl(StructuralNode parent, Map attributes) { + super(parent, CONTEXT, attributes); + } + + public ParagraphBlockImpl(StructuralNode parent, Map attributes, Object content) { + super(parent, CONTEXT, attributes, content); + } + + public ParagraphBlockImpl(StructuralNode parent, Map attributes, List roles, + Object content, List blocks, String contentModel, List subs) { + super(parent, CONTEXT, attributes, roles, content, blocks, contentModel, subs); + } + + public ParagraphBlockImpl(StructuralNode parent, Map attributes, List roles, + Object content, List blocks, Integer level, String contentModel, + List subs) { + super(parent, CONTEXT, attributes, roles, content, blocks, level, contentModel, subs); + } +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/PhraseNodeImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/PhraseNodeImpl.java new file mode 100644 index 00000000..963788cc --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/PhraseNodeImpl.java @@ -0,0 +1,50 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import io.github.swagger2markup.adoc.AsciidocConverter; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.PhraseNode; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class PhraseNodeImpl extends ContentNodeImpl implements PhraseNode { + + private final String type; + private final String text; + private final String target; + private final AsciidocConverter converter = new AsciidocConverter(AsciidocConverter.NAME, new HashMap<>()); + + public PhraseNodeImpl(ContentNode parent, String context, Map attributes, List roles, String type, String text, String target) { + super(parent, context, attributes, roles); + this.type = type; + this.text = text; + this.target = target; + } + + @Override + @Deprecated + public String render() { + return convert(); + } + + @Override + public String convert() { + return converter.convert(this, null, new HashMap<>()); + } + + @Override + public String getType() { + return type; + } + + @Override + public String getText() { + return text; + } + + @Override + public String getTarget() { + return target; + } +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/RowImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/RowImpl.java new file mode 100644 index 00000000..02f4c29d --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/RowImpl.java @@ -0,0 +1,21 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.asciidoctor.ast.Cell; +import org.asciidoctor.ast.Row; + +import java.util.List; + +public class RowImpl implements Row { + + private final List cells; + + public RowImpl(List cells) { + this.cells = cells; + } + + @Override + public List getCells() { + return cells; + } + +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/SectionImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/SectionImpl.java new file mode 100644 index 00000000..6e0fa3af --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/SectionImpl.java @@ -0,0 +1,142 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.asciidoctor.ast.Section; +import org.asciidoctor.ast.StructuralNode; + +import java.util.List; +import java.util.Map; + +public class SectionImpl extends StructuralNodeImpl implements Section { + + private final Integer index; + private final Integer number; + private final String numeral; + private final String sectionName; + private final boolean special; + private final boolean numbered; + + public SectionImpl(StructuralNode parent) { + super(parent, "section"); + this.index = null; + this.number = null; + this.numeral = ""; + this.sectionName = ""; + this.special = false; + this.numbered = false; + } + + public SectionImpl(StructuralNode parent, Map attributes) { + super(parent, "section", attributes); + this.index = null; + this.number = null; + this.numeral = ""; + this.sectionName = ""; + this.special = false; + this.numbered = false; + } + + + public SectionImpl(StructuralNode parent, String context, Object content, String sectionName) { + super(parent, context, content); + this.index = null; + this.number = null; + this.numeral = ""; + this.sectionName = sectionName; + this.special = false; + this.numbered = false; + } + + public SectionImpl(StructuralNode parent, String context, Object content, int index, int number, String numeral, String sectionName, boolean special, boolean numbered) { + super(parent, context, content); + this.index = index; + this.number = number; + this.numeral = numeral; + this.sectionName = sectionName; + this.special = special; + this.numbered = numbered; + } + + public SectionImpl(StructuralNode parent, String context, Map attributes, List roles, Object content, List blocks, String contentModel, List subs, int index, int number, String numeral, String sectionName, boolean special, boolean numbered) { + super(parent, context, attributes, roles, content, blocks, contentModel, subs); + this.index = index; + this.number = number; + this.numeral = numeral; + this.sectionName = sectionName; + this.special = special; + this.numbered = numbered; + } + + public SectionImpl(StructuralNode parent, String context, Map attributes, List roles, + Object content, List blocks, Integer level, String contentModel, List subs, + int index, int number, String numeral, String sectionName, boolean special, boolean numbered) { + super(parent, context, attributes, roles, content, blocks, level, contentModel, subs); + this.index = index; + this.number = number; + this.numeral = numeral; + this.sectionName = sectionName; + this.special = special; + this.numbered = numbered; + } + + @Override + @Deprecated + public int index() { + return getIndex(); + } + + @Override + public int getIndex() { + return index; + } + + @Override + @Deprecated + public int number() { + return getNumber(); + } + + @Override + @Deprecated + public int getNumber() { + return number; + } + + @Override + public String getNumeral() { + return numeral; + } + + @Override + @Deprecated + public String sectname() { + return getSectionName(); + } + + @Override + public String getSectionName() { + return sectionName; + } + + @Override + @Deprecated + public boolean special() { + return isSpecial(); + } + + @Override + public boolean isSpecial() { + return special; + } + + @Override + @Deprecated + public boolean numbered() { + return isNumbered(); + } + + @Override + public boolean isNumbered() { + return numbered; + } + +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/StructuralNodeImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/StructuralNodeImpl.java new file mode 100644 index 00000000..293b5098 --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/StructuralNodeImpl.java @@ -0,0 +1,179 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import io.github.swagger2markup.adoc.AsciidocConverter; +import org.asciidoctor.ast.Cursor; +import org.asciidoctor.ast.StructuralNode; + +import java.util.*; + +public class StructuralNodeImpl extends ContentNodeImpl implements StructuralNode { + + private String title; + private String caption; + private String style; + private final Object content; + private final List blocks; + private Integer level; + private final String contentModel; + private List subs; + private final AsciidocConverter converter = new AsciidocConverter(AsciidocConverter.NAME, new HashMap<>()); + + public StructuralNodeImpl(StructuralNode parent, String context) { + this(parent, context, new HashMap<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), "", new ArrayList<>()); + } + + public StructuralNodeImpl(StructuralNode parent, String context, Map attributes) { + this(parent, context, attributes, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), "", new ArrayList<>()); + } + + public StructuralNodeImpl(StructuralNode parent, String context, Object content) { + this(parent, context, new HashMap<>(), new ArrayList<>(), content, new ArrayList<>(), "", new ArrayList<>()); + } + + public StructuralNodeImpl(StructuralNode parent, String context, Object content, Map attributes) { + this(parent, context, attributes, new ArrayList<>(), content, new ArrayList<>(), "", new ArrayList<>()); + } + + public StructuralNodeImpl(StructuralNode parent, String context, Map attributes, List roles, + Object content, List blocks, String contentModel, List subs) { + this(parent, context, attributes, roles, content, blocks, calculateLevel(parent), contentModel, subs); + } + + public StructuralNodeImpl(StructuralNode parent, String context, Map attributes, List roles, + Object content, List blocks, Integer level, String contentModel, List subs) { + super(parent, context, attributes, roles); + this.content = content; + this.blocks = blocks; + this.level = level; + this.contentModel = contentModel; + this.subs = subs; + } + + @Override + @Deprecated + public String title() { + return getTitle(); + } + + @Override + public String getTitle() { + return title; + } + + @Override + public void setTitle(String title) { + this.title = title; + } + + @Override + public String getCaption() { + return caption; + } + + @Override + public void setCaption(String caption) { + this.caption = caption; + } + + @Override + @Deprecated + public String style() { + return getStyle(); + } + + @Override + public String getStyle() { + return style; + } + + @Override + public void setStyle(String style) { + this.style = style; + } + + @Override + @Deprecated + public List blocks() { + return getBlocks(); + } + + @Override + public List getBlocks() { + return blocks; + } + + @Override + public void append(StructuralNode block) { + blocks.add(block); + } + + @Override + @Deprecated + public Object content() { + return getContent(); + } + + @Override + public Object getContent() { + return content; + } + + @Override + public String convert() { + return converter.convert(this, null, new HashMap<>()); + } + + @Override + public int getLevel() { + return level; + } + + @Override + public Cursor getSourceLocation() { + return new CursorImpl(); + } + + @Override + public String getContentModel() { + return contentModel; + } + + @Override + public List getSubstitutions() { + return subs; + } + + @Override + public boolean isSubstitutionEnabled(String substitution) { + return subs.contains(substitution); + } + + @Override + public void removeSubstitution(String substitution) { + subs.remove(substitution); + } + + @Override + public void addSubstitution(String substitution) { + subs.add(substitution); + } + + @Override + public void prependSubstitution(String substitution) { + } + + @Override + public void setSubstitutions(String... substitutions) { + subs = Arrays.asList(substitutions); + } + + @Override + public List findBy(Map selector) { + throw new UnsupportedOperationException("Not implemented, yet"); + } + + protected static Integer calculateLevel(StructuralNode parent) { + return null != parent ? parent.getLevel() + 1 : 0; + } + +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/TableImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/TableImpl.java new file mode 100644 index 00000000..80a19666 --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/TableImpl.java @@ -0,0 +1,306 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.asciidoctor.ast.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.*; + +public class TableImpl extends StructuralNodeImpl implements Table { + public static final String OPTION_UNBREAKABLE = "unbreakable"; + public static final String OPTION_BREAKABLE = "breakable"; + private Logger logger = LoggerFactory.getLogger(getClass()); + public static final String CONTEXT = "table"; + private static final String FRAME_ATTR = "frame"; + private static final String GRID_ATTR = "grid"; + private RowList headerRows; + private RowList bodyRows; + private RowList footerRows; + + private List columns = new ArrayList<>(); + + public TableImpl(StructuralNode parent) { + this(parent, new HashMap<>(), new ArrayList<>()); + } + + public TableImpl(StructuralNode parent, Map attributes, List roles) { + this(parent, attributes, roles, calculateLevel(parent)); + } + + public TableImpl(StructuralNode parent, Map attributes, List roles, Integer level) { + this(parent, attributes, roles, null, new ArrayList<>(), level, "", new ArrayList<>()); + } + + public TableImpl(StructuralNode parent, Map attributes, List roles, + Object content, List blocks, Integer level, String contentModel, List subs) { + super(parent, CONTEXT, attributes, roles, content, blocks, level, contentModel, subs); + this.headerRows = new RowList(new ArrayList<>()); + this.bodyRows = new RowList(new ArrayList<>()); + this.footerRows = new RowList(new ArrayList<>()); + } + + @Override + public boolean hasHeaderOption() { + return isOption("header"); + } + + @Override + public String getFrame() { + return (String) getAttribute(FRAME_ATTR, "all"); + } + + @Override + public void setFrame(String frame) { + setAttribute(FRAME_ATTR, frame, true); + } + + @Override + public String getGrid() { + return (String) getAttribute(GRID_ATTR, "all"); + } + + @Override + public void setGrid(String grid) { + setAttribute(GRID_ATTR, grid, true); + } + + @Override + public List getColumns() { + return columns; + } + + @Override + public List getHeader() { + return headerRows; + } + + public void setHeaderRow(Row row) { + headerRows.clear(); + headerRows.add(row); + scanRowForColumns(row); + } + + public void setHeaderRow(List cells) { + setHeaderRow(new RowImpl(cells)); + } + + public void setHeaderRow(String... documentContents) { + headerRows.clear(); + headerRows.add(generateRow(documentContents)); + } + + public RowImpl generateRow(Document... innerDocs) { + List cells = new ArrayList<>(); + for (int i = 0; i < innerDocs.length; i++) { + + Column column = null; + try { + column = columns.get(i); + } catch (Exception ignored) { + } + + if (null == column) { + ColumnImpl newColumn = new ColumnImpl(this); + newColumn.setColumnNumber(i + 1); + column = newColumn; + addColumnAt(column, i); + } + cells.add(new CellImpl(column, innerDocs[i])); + + } + return new RowImpl(cells); + } + + public RowImpl generateRow(String... documentContents) { + Document[] documents = Arrays.stream(documentContents).map(documentContent -> { + Document innerDoc = new DocumentImpl(); + Block paragraph = new ParagraphBlockImpl(innerDoc); + paragraph.setSource(documentContent); + innerDoc.append(paragraph); + return innerDoc; + }).toArray(Document[]::new); + return generateRow(documents); + } + + @Override + public List getBody() { + return bodyRows; + } + + public void setBodyRows(List rows) { + bodyRows.clear(); + bodyRows.addAll(rows); + bodyRows.forEach(this::scanRowForColumns); + } + + public void addRow(Row row) { + bodyRows.add(row); + scanRowForColumns(row); + } + + public void addRow(List cells) { + bodyRows.add(new RowImpl(cells)); + } + + public RowImpl addRow(Document... documentContents) { + RowImpl row = generateRow(documentContents); + bodyRows.add(row); + return row; + } + + public RowImpl addRow(String... documentContents) { + RowImpl row = generateRow(documentContents); + bodyRows.add(row); + return row; + } + + @Override + public List getFooter() { + return footerRows; + } + + public void setFooterRow(Row row) { + footerRows.clear(); + footerRows.add(row); + scanRowForColumns(row); + } + + public void setFooterRow(String... documentContents) { + footerRows.clear(); + footerRows.add(generateRow(documentContents)); + } + + private void scanRowForColumns(Row row) { + row.getCells().forEach(cell -> { + Column column = cell.getColumn(); + int i = column.getColumnNumber() - 1; + addColumnAt(column, i); + }); + } + + private void addColumnAt(Column column, int i) { + if (columns.size() >= i) { + columns.add(i, column); + } else { + while (columns.size() < i) { + columns.add(columns.size(), null); + } + columns.add(column); + } + } + + public void setFooterRow(List cells) { + setFooterRow(new RowImpl(cells)); + } + + class RowList extends AbstractList { + + private final List rubyArray; + + private RowList(List rubyArray) { + this.rubyArray = rubyArray; + } + + @Override + public int size() { + return rubyArray.size(); + } + + @Override + public boolean isEmpty() { + return rubyArray.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return rubyArray.contains(o); + } + + @Override + public boolean add(Row row) { + boolean changed = false; + try { + changed = rubyArray.add(row); + setAttribute("rowcount", size(), true); + } catch (Exception e) { + logger.debug("Couldn't add row", e); + } + return changed; + } + + @Override + public boolean remove(Object o) { + if (!(o instanceof RowImpl)) { + return false; + } + try { + boolean changed = rubyArray.remove(o); + setAttribute("rowcount", size(), true); + return changed; + } catch (Exception e) { + logger.debug("Couldn't add row", e); + return false; + } + } + + @Override + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + rubyArray.clear(); + setAttribute("rowcount", size(), true); + } + + @Override + public Row get(int index) { + return rubyArray.get(index); + } + + @Override + public Row set(int index, Row element) { + Row oldRow = get(index); + rubyArray.set(index, element); + return oldRow; + } + + @Override + public void add(int index, Row element) { + rubyArray.add(index, element); + setAttribute("rowcount", size(), true); + } + + @Override + public Row remove(int index) { + Row removed = rubyArray.remove(index); + setAttribute("rowcount", size(), true); + return removed; + } + + @Override + public int indexOf(Object o) { + if (!(o instanceof RowImpl)) { + return -1; + } + return rubyArray.indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + if (!(o instanceof RowImpl)) { + return -1; + } + return rubyArray.lastIndexOf(o); + } + } + + protected static Integer calculateLevel(StructuralNode parent) { + int level = 1; + if (parent instanceof Table) + level = parent.getLevel() + 1; + return level; + } +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/TitleImpl.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/TitleImpl.java new file mode 100644 index 00000000..ed594e04 --- /dev/null +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/ast/impl/TitleImpl.java @@ -0,0 +1,34 @@ +package io.github.swagger2markup.adoc.ast.impl; + +import org.asciidoctor.ast.Title; + +public class TitleImpl implements Title { + + private final String main; + private final String subtitle; + + public TitleImpl(String main, String subtitle) { + this.main = main; + this.subtitle = subtitle; + } + + @Override + public String getMain() { + return main; + } + + @Override + public String getSubtitle() { + return subtitle; + } + + @Override + public String getCombined() { + return main + ": " + subtitle; + } + + @Override + public boolean isSanitized() { + return false; + } +} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/AsciiDocConverterRegistry.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/AsciiDocConverterRegistry.java deleted file mode 100644 index 82cebf90..00000000 --- a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/AsciiDocConverterRegistry.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.swagger2markup.adoc.converter; - -import org.asciidoctor.Asciidoctor; -import org.asciidoctor.jruby.converter.spi.ConverterRegistry; - -public class AsciiDocConverterRegistry implements ConverterRegistry { - @Override - public void register(Asciidoctor asciidoctor) { - asciidoctor.javaConverterRegistry().register(AsciidocConverter.class); - } -} diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/internal/DelimitedBlockNode.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/internal/DelimitedBlockNode.java index 9ecf84a1..64c05140 100644 --- a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/internal/DelimitedBlockNode.java +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/internal/DelimitedBlockNode.java @@ -3,6 +3,9 @@ import org.apache.commons.lang3.StringUtils; import org.asciidoctor.ast.StructuralNode; +import java.util.ArrayList; +import java.util.List; + import static io.github.swagger2markup.adoc.converter.internal.Delimiters.*; public class DelimitedBlockNode extends ParagraphAttributes { @@ -15,12 +18,14 @@ public DelimitedBlockNode(StructuralNode node) { public void processPositionalAttributes() { String source = pop("1", "style"); StringBuilder options = new StringBuilder(); + List toRemove = new ArrayList<>(); attributes.forEach((k, v) -> { if (k.endsWith(OPTION_SUFFIX)) { - attributes.remove(k); + toRemove.add(k); options.append('%').append(k.replace(OPTION_SUFFIX, "")); } }); + toRemove.forEach(attributes::remove); source += options.toString(); if (StringUtils.isNotBlank(source)) { diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/internal/Delimiters.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/internal/Delimiters.java index 8b342b69..788351eb 100644 --- a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/internal/Delimiters.java +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/internal/Delimiters.java @@ -5,18 +5,20 @@ public class Delimiters { public static final String ATTRIBUTES_END = "]"; public static final String COLON = ":"; public static final String DELIMITER_BLOCK = "----"; - public static final String DELIMITER_CELL = "|"; public static final String DELIMITER_EXAMPLE = "===="; + public static final String DELIMITER_INNER_TABLE_CELL = "!"; + public static final String DELIMITER_INNER_TABLE = "!==="; public static final String DELIMITER_PAGE_BREAK = "<<<"; public static final String DELIMITER_OPEN_BLOCK = "--"; public static final String DELIMITER_SIDEBAR = "****"; public static final String DELIMITER_TABLE = "|==="; + public static final String DELIMITER_TABLE_CELL = "|"; public static final String DELIMITER_THEMATIC_BREAK = "'''"; public static final String DELIMITER_VERSE = "____"; - public static final String DOCUMENT_TITLE = "= "; + public static final String DOCUMENT_TITLE = "="; public static final String LINE_SEPARATOR = "\n"; public static final String MARKER_LIST_ITEM = "*"; - public static final String MARKER_D_LIST_ITEM = "::"; + public static final String MARKER_D_LIST_ITEM = ":"; public static final String STYLE_HORIZONTAL = "horizontal"; public static final String STYLE_Q_AND_A = "qanda"; public static final String STYLE_SOURCE = "source"; diff --git a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/internal/SourceNode.java b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/internal/SourceNode.java index 5593b527..69339f1c 100644 --- a/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/internal/SourceNode.java +++ b/swagger2markup-asciidoc/src/main/java/io/github/swagger2markup/adoc/converter/internal/SourceNode.java @@ -23,12 +23,14 @@ public void processPositionalAttributes() { String source = pop("1", "style"); String language = pop("2", "language"); StringBuilder options = new StringBuilder(); + List toRemove = new ArrayList<>(); attributes.forEach((k, v) -> { if (k.endsWith(OPTION_SUFFIX)) { - attributes.remove(k); + toRemove.add(k); options.append('%').append(k.replace(OPTION_SUFFIX, "")); } }); + toRemove.forEach(attributes::remove); source += options.toString(); if (StringUtils.isNotBlank(source)) { diff --git a/swagger2markup-asciidoc/src/main/resources/servivces/org.asciidoctor.jruby.converter.spi.ConverterRegistry b/swagger2markup-asciidoc/src/main/resources/servivces/org.asciidoctor.jruby.converter.spi.ConverterRegistry index 88c065f3..815ad0bc 100644 --- a/swagger2markup-asciidoc/src/main/resources/servivces/org.asciidoctor.jruby.converter.spi.ConverterRegistry +++ b/swagger2markup-asciidoc/src/main/resources/servivces/org.asciidoctor.jruby.converter.spi.ConverterRegistry @@ -1 +1 @@ -io.github.swagger2markup.adoc.converter.AsciiDocConverterRegistry +io.github.swagger2markup.adoc.AsciiDocConverterRegistry diff --git a/swagger2markup-asciidoc/src/test/java/io/github/swagger2markup/adoc/converter/AsciidocConverterTest.java b/swagger2markup-asciidoc/src/test/java/io/github/swagger2markup/adoc/AsciidocConverterTest.java similarity index 92% rename from swagger2markup-asciidoc/src/test/java/io/github/swagger2markup/adoc/converter/AsciidocConverterTest.java rename to swagger2markup-asciidoc/src/test/java/io/github/swagger2markup/adoc/AsciidocConverterTest.java index 8e64f2b0..30ea9e42 100644 --- a/swagger2markup-asciidoc/src/test/java/io/github/swagger2markup/adoc/converter/AsciidocConverterTest.java +++ b/swagger2markup-asciidoc/src/test/java/io/github/swagger2markup/adoc/AsciidocConverterTest.java @@ -1,4 +1,4 @@ -package io.github.swagger2markup.adoc.converter; +package io.github.swagger2markup.adoc; import org.apache.commons.io.IOUtils; import org.asciidoctor.Asciidoctor; @@ -18,9 +18,10 @@ public class AsciidocConverterTest { private Asciidoctor asciidoctor = Asciidoctor.Factory.create(); - @Parameterized.Parameters + @Parameterized.Parameters(name = "Run {index}: file={0}") public static Iterable data() { return Arrays.asList( + "simple.adoc", "arrows-and-boxes-example.ad", "brokeninclude.asciidoc", "changeattribute.adoc", diff --git a/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/arrows-and-boxes-example.ad b/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/arrows-and-boxes-example.ad index 48c762fc..673ff0b2 100644 --- a/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/arrows-and-boxes-example.ad +++ b/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/arrows-and-boxes-example.ad @@ -31,3 +31,4 @@ [arrowsAndBoxes] (User) > (Admin) + diff --git a/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/brokeninclude.asciidoc b/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/brokeninclude.asciidoc index e0e0fb4d..89c7c6cd 100644 --- a/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/brokeninclude.asciidoc +++ b/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/brokeninclude.asciidoc @@ -30,3 +30,4 @@ :attribute-undefined: drop-line link:b.adoc[] + diff --git a/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/changeattribute.adoc b/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/changeattribute.adoc index ce4f1696..df78baea 100644 --- a/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/changeattribute.adoc +++ b/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/changeattribute.adoc @@ -30,3 +30,4 @@ :attribute-undefined: drop-line sample {content} + diff --git a/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/chronicles-example.adoc b/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/chronicles-example.adoc index 46240a22..09ab7923 100644 --- a/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/chronicles-example.adoc +++ b/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/chronicles-example.adoc @@ -1,7 +1,6 @@ = The Dangerous & _Thrilling_ Documentation Chronicles: Based on True Events -Kismet Caméléon, Lazarus het Draeke +Kismet Caméléon; Lazarus het Draeke v1.0, 2014-01-01 - :tip-caption: Tip :appendix-caption: Appendix :toclevels: 3 @@ -57,8 +56,8 @@ v1.0, 2014-01-01 [abstract] {description} +[[_its_a_city_under_siege]] == It’s a City Under Siege - This journey begins one late Monday afternoon at {uri-devoxx}[((Devoxx))]. Our team needs coffee, _desperately_, but none of us dare open the theater doors... @@ -82,18 +81,16 @@ Quick, hit kbd:[Ctrl,Alt,Backspace] or select menu:File[Quit] and let's bail out WARNING: Working with DefOps{empty}footnote:defops[] werewolves leads to howling and trying to train aggressive regular expressions with Pavlovian reinforcement. _Weak light from the hallway trickled across the theater, chased by a distant scream._ +[[_rendezvous_point]] === Rendezvous Point - Come on, [[bier-central,Bier Central]]_Bier Central_, of course! Did you have to ask? If you beat me there, order me a {uri-stbernardusabt12}[St. Bernardus Abt 12]. Here's some €. - - +[[ravages]] [#ravages] == The Ravages of Writing - Crystalline XML tags relentlessly bombarded the theater. .XML tags @@ -107,7 +104,6 @@ Crystalline XML tags relentlessly bombarded the theater. ---- - Despite the assault, we continued our pursuit to draft a DefOps{empty}footnote:defops[] plan. .DefOps Plan @@ -119,11 +115,10 @@ Somebody please save us now! I want my mum -- and an extra-large double macchiato, please. ==== +Unfortunately, Lazarus and I had both come to the conclusion that we weren't going to get out of this without corrupted hardrives if we didn't locate caffeine within the next few hours. -Unfortunaly, Lazarus and I had both come to the conclusion that we weren't going to get out of this without corrupted hardrives if we didn't locate caffeine within the next few hours. - +[[_a_recipe_for_potion_that_will_ensure_you_win_the_hearts_of_developers]] === A Recipe for Potion That Will Ensure You Win the Hearts of Developers - This potion for a sample document contains the following ingredients, which are listed in a very random, chaotically nested order. .Ingredients for Potion that Demystifies Documents @@ -131,187 +126,93 @@ This potion for a sample document contains the following ingredients, which are ** syntax highlighted source code *** non-syntax highlighted source code or just a listing block - - - - - - * quote block ** verse block *** table with some cell formatting **** sequential paragraphs ***** admonition blocks, but use them sparingly - - - - - - *** bullet list with nesting - - - ** numbered list with nesting - ** definition list *** sidebar - - - - - - * example block ** block image (no inline images) *** inline formatting in a paragraph **** two fresh Burdockian leaves ***** They must be harvested by the light of the teal moons. - - - - - - - - - - - - - - Are you square? [square] * one - * two - * three - - What is there to do? * [x] Done - * [ ] Next - * Who's counting? - - +[[_searching_for_burdockian]] ==== Searching for Burdockian - .Steps for finding and preparing Burdockian leaves . Locate dusty botany .. Sneeze ... Sneeze some more - - - - - - . Find section on Burdockian .. Review its characteristics ... Take a picture of the diagram of its leaves .... Don't rip out the picture like a troglodyte ..... Don't do it, I'm watching you - - - - - - - - - - - - . Put on your hiking boots - . Freeze your butt off on the side of a mountain at midnight - - Let's skip a few steps and start counting from 10. [start=10] . arabic (10) .. loweralpha (a) ... lowerroman (i) - ... lowerroman (ii) - ... lowerroman (iii) - ... lowerroman (iv) .... upperalpha (A) - - - - - - - - - . arabic (11) - - It's time for a top 5 list, made using the `reversed` option on an ordered list! [%reversed] . Stone Imperial Russian Stout - . Pliny the Elder - . Chimay Grande Réserve (Blue) - . St. Bernardus Abt 12 - . Westvleteren 12 (XII) - - How about a list with some terms? * Fruits -Apple:: - +Apple:::: The round fruit of a tree of the rose family, which typically has thin red or green skin and crisp flesh. Yes, I said _flesh_. -Pear:: - +Pear:::: A yellowish- or brownish-green edible fruit that is typically narrow at the stalk and wider toward the base, with sweet, slightly gritty flesh. More flesh. Mmmmm. - - * Vegetables -Carrot:: - +Carrot:::: An orange-colored root eaten as a vegetable. Beware, it's a favorite of the Wolpertinger. - - - - +[[_are_you_still_here]] ===== Are You Still Here? - .Move, move, move! [CAUTION] ==== @@ -319,17 +220,12 @@ The Wolpertingers can smell your procrastination. It's not their fault you can't find your boots. ==== - +[[_sigh]] ====== Sigh…​ - TIP: Your boots are in your closet. - - - - +[[_dawn_on_the_plateau]] == Dawn on the Plateau - Lazarus was hanging from the bottom limb of a Burdockian tree, licking the bark. [quote,Mark Tobey] @@ -345,16 +241,14 @@ No bark was harmed in the making of this potion. Crap, I smell an injunction. ____ - We'd retrieved the leaves, but we'd obviously lost our minds in the process. [verse] Roses are +++red+++. Violets are +++blue+++__-ish__. - +[[_words_seasoned_with_power]] == Words Seasoned with Power - To _tame_ the [.wild]#wild# wolpertingers, we needed to build a *charm*. But **ul**timate victory could only be won if we divined the *_true name_* of the __war__lock. @@ -366,8 +260,8 @@ Kizmet shrugged. "`The note from Olaf's desk says '`wormwood and licorice,`' but "`Wait!`" Indigo plucked a small vial from her desk's top drawer and held it toward us. The vial's label read '```e = mc^2^ *_the scent of science_* _smells like a genius_```'. +[[_can_i_get_some_code]] === Can I Get Some `Code`? - [%hardbreaks] Sure. Have a listing block. @@ -378,7 +272,6 @@ Have a listing block. This is an example of a listing block. The content inside is rendered as

 text.
 ----
-
 But I'm not giving you any highlighting shazam just yet.
 
 .What is a listing block?
@@ -387,16 +280,13 @@ Listing block content is rendered as `
` text.
 
 The `listing` style is applied to an element, such as a paragraph, by setting the `listing` attribute on that element.
 
-
 Let's get our #((highlighting))# on!
 
 <<<
-
 Install Prawn:
 
 [literal]
 $ gem install prawn
-
 Then create your first PDF document in Ruby!
 
 .Generates a basic PDF document using Prawn
@@ -409,14 +299,9 @@ Prawn::Document.generate 'output.pdf' do # <3>
   text 'Hello, World!' # <2>
 end
 ----
-
 <1> Imports Prawn library
-
 <3> Adds text “Hello, World!” to first page
-
 <2> Writes PDF to [file]_output.pdf_ after executing all statements
-
-
 How about some source code that styles code? So meta!
 
 [source,css]
@@ -432,7 +317,6 @@ code {
   border-radius: 4px;
 }
 ----
-
 Where could we go without some Java(TM)?
 Naturally, some autosizing is necessary.
 
@@ -493,7 +377,6 @@ public class GreetingReceiver implements EventReceiver, Serializable {
 
 }
 ----
-
 We already showed you an XML example in <>, a language we often rant about over beers at <>.
 
 I'll trade you a little table for some of that bark.
@@ -513,24 +396,19 @@ I'll trade you a little table for some of that bark.
 4+^.`::
-
+``:::
 an html tag that makes me crazy
-align::
-
+align:::
 something I never get going in the right direction.
 Also has to do with my poor verbal communication skills
-float::
-style::
-
+float:::
+style:::
 don't make me laugh
 
-
 Does anyone have the time?
 
 Tg lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
@@ -538,10 +416,8 @@ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliqu
 Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
 Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborumj.
 
-
-
+[[_keeping_it_together]]
 == Keeping It Together
-
 On this page we have nested "`keep together`" logic.
 The combined block will be shifted to the next page if there isn't room available on this one.
 
@@ -565,7 +441,6 @@ to
 its
 breaking
 point.
-
 .What happens if there is both a field and a method with the same name?
 [NOTE]
 ====
@@ -583,7 +458,6 @@ public class Foo {
   }
 }
 ----
-
 *Golo resolves methods first, fields last.*
 Hence, the following Golo code will resolve the `bar()` method, not the `bar` field:
 
@@ -597,59 +471,45 @@ foo: bar("baz") # <1>
 
 println(foo: bar()) # <2>
 ----
-
 <1> Writes the field
-
 <2> Calls the `bar()` method
-
-
 ====
-
 <<<
-
 Here's a preview of how each heading level is rendered.
 
 [discrete]
 = Heading 1 (Level 0)
-
 filler content
 
 [discrete]
 == Heading 2 (Level 1)
-
 filler content
 
 [discrete]
 === Heading 3 (Level 2)
-
 filler content
 
 [discrete]
 ==== Heading 4 (Level 3)
-
 filler content
 
 [discrete]
 ===== Heading 5 (Level 4)
-
 filler content
 
 [discrete]
 ====== Heading 6 (Level 5)
-
 filler content
 
 '''
-
 --
 Here's some content inside an open block.
 
 --
 
-
+[[_credits]]
 [appendix]
 == Credits
-
 .Brought to you with icon:heart[set=fas,role=love] by OpenDevise
 [table%footer%header,grid=rows,width=75%,cols="2,2s,^4",frame=topbot]
 |===
@@ -669,7 +529,7 @@ Here's some content inside an open block.
 
 |===
 
-
+[[_index]]
 [index]
 == Index
 
diff --git a/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/document-with-arrays.adoc b/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/document-with-arrays.adoc
index 95f66e4c..603e7a7e 100644
--- a/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/document-with-arrays.adoc
+++ b/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/document-with-arrays.adoc
@@ -31,31 +31,18 @@
 
 http://asciidoctor.org[Asciidoctor] is an open source text processor and publishing toolchain for converting http://asciidoctor.org[AsciiDoc] markup into HTML, DocBook and custom formats.
 
+
 This document provides a high-level view of the changes introduced in Asciidoctor by release.
 For a detailed view of what has changed, refer to the https://github.com/asciidoctor/asciidoctor/commits/master[commit history] on GitHub.
 
+[[_0_1_4_2013_09_05_mojavelinux]]
 == 0.1.4 (2013-09-05) - @mojavelinux
-
 Performance::
-
 * 15% increase in speed compared to 0.1.3
 
-
-
-
 Enhancements::
-
 * updated xref inline macro to support inter-document references (#417)
 
-
-
-
 Bug Fixes::
-
 * lowercase attribute names passed to API (#508)
 
-
-
-
-
-
diff --git a/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/simple.adoc b/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/simple.adoc
new file mode 100644
index 00000000..0fe4a445
--- /dev/null
+++ b/swagger2markup-asciidoc/src/test/resources/asciidoc/expected/simple.adoc
@@ -0,0 +1,77 @@
+= Simple Inventory API
+You 
+v1.0.0
+:tip-caption: Tip
+:appendix-caption: Appendix
+:appendix-refsig: Appendix
+:authorinitials: Y
+:toc-title: Table of Contents
+:iconsdir: ./images/icons
+:author: You
+:warning-caption: Warning
+:figure-caption: Figure
+:attribute-missing: skip
+:section-refsig: Section
+:toc-placement: auto
+:important-caption: Important
+:authors: You
+:note-caption: Note
+:firstname: You
+:stylesdir: .
+:untitled-label: Untitled
+:max-include-depth: 64
+:caution-caption: Caution
+:user-home: .
+:max-attribute-value-size: 4096
+:safe-mode-level: 20
+:safe-mode-name: secure
+:table-caption: Table
+:part-refsig: Part
+:authorcount: 1
+:example-caption: Example
+:email: you@your-company.com
+:version-label: Version
+:revnumber: 1.0.0
+:last-update-label: Last updated
+:doctype: article
+:chapter-refsig: Chapter
+:attribute-undefined: drop-line
+
+[[_overview]]
+== Overview
+This is a simple API
+
+[[_license]]
+=== License
+[%hardbreaks]
+http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0]
+
+[[_servers]]
+== Servers
+* https://{username}.gigantic-server.com:{port}/{basePath}
++
+The production API server
+
++
+.Variables
+username::
+*this* __value__ is assigned by the service provider, in this example `gigantic-server.com`
+Possible Values::
+Any
+Default::
+demo
+
+port::
+Possible Values::
+- 8443
+- 443
+
+Default::
+8443
+
+basePath::
+Possible Values::
+Any
+Default::
+v2
+
diff --git a/swagger2markup-asciidoc/src/test/resources/asciidoc/original/chronicles-example.adoc b/swagger2markup-asciidoc/src/test/resources/asciidoc/original/chronicles-example.adoc
index b0c3d267..8f8d2b7a 100644
--- a/swagger2markup-asciidoc/src/test/resources/asciidoc/original/chronicles-example.adoc
+++ b/swagger2markup-asciidoc/src/test/resources/asciidoc/original/chronicles-example.adoc
@@ -109,7 +109,7 @@ Somebody please save us now!
 I want my mum -- and an extra-large double macchiato, please.
 ====
 
-Unfortunaly, Lazarus and I had both come to the conclusion that we weren't going to get out of this without corrupted hardrives if we didn't locate caffeine within the next few hours.
+Unfortunately, Lazarus and I had both come to the conclusion that we weren't going to get out of this without corrupted hardrives if we didn't locate caffeine within the next few hours.
 
 === A Recipe for Potion That Will Ensure You Win the Hearts of Developers
 
diff --git a/swagger2markup-asciidoc/src/test/resources/asciidoc/original/simple.adoc b/swagger2markup-asciidoc/src/test/resources/asciidoc/original/simple.adoc
new file mode 100644
index 00000000..a6659110
--- /dev/null
+++ b/swagger2markup-asciidoc/src/test/resources/asciidoc/original/simple.adoc
@@ -0,0 +1,28 @@
+= Simple Inventory API
+You 
+v1.0.0
+
+== Overview
+This is a simple API
+
+=== License
+[%hardbreaks]
+http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0]
+
+== Servers
+* https://{username}.gigantic-server.com:{port}/{basePath}
++
+The production API server
++
+.Variables
+username:: *this* __value__ is assigned by the service provider, in this example `gigantic-server.com`
+  Possible Values::: Any
+  Default::: demo
+port::
+  Possible Values:::
+  - 8443
+  - 443
+  Default::: 8443
+basePath::
+  Possible Values::: Any
+  Default::: v2
diff --git a/swagger2markup-asciidoc/src/test/resources/logback.xml b/swagger2markup-asciidoc/src/test/resources/logback.xml
new file mode 100644
index 00000000..5666f523
--- /dev/null
+++ b/swagger2markup-asciidoc/src/test/resources/logback.xml
@@ -0,0 +1,14 @@
+
+
+    
+        
+            %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+        
+    
+
+    
+
+    
+        
+    
+
diff --git a/swagger2markup-asciidoc/src/test/resources/yaml/swagger_petstore.yaml b/swagger2markup-asciidoc/src/test/resources/yaml/swagger_petstore.yaml
deleted file mode 100644
index 10f4c499..00000000
--- a/swagger2markup-asciidoc/src/test/resources/yaml/swagger_petstore.yaml
+++ /dev/null
@@ -1,111 +0,0 @@
-openapi: "3.0.0"
-info:
-  version: 1.0.0
-  title: Swagger Petstore
-  license:
-    name: MIT
-servers:
-  - url: http://petstore.swagger.io/v1
-paths:
-  /pets:
-    get:
-      summary: List all pets
-      operationId: listPets
-      tags:
-        - pets
-      parameters:
-        - name: limit
-          in: query
-          description: How many items to return at one time (max 100)
-          required: false
-          schema:
-            type: integer
-            format: int32
-      responses:
-        '200':
-          description: A paged array of pets
-          headers:
-            x-next:
-              description: A link to the next page of responses
-              schema:
-                type: string
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/Pets"
-        default:
-          description: unexpected error
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/Error"
-    post:
-      summary: Create a pet
-      operationId: createPets
-      tags:
-        - pets
-      responses:
-        '201':
-          description: Null response
-        default:
-          description: unexpected error
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/Error"
-  /pets/{petId}:
-    get:
-      summary: Info for a specific pet
-      operationId: showPetById
-      tags:
-        - pets
-      parameters:
-        - name: petId
-          in: path
-          required: true
-          description: The id of the pet to retrieve
-          schema:
-            type: string
-      responses:
-        '200':
-          description: Expected response to a valid request
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/Pet"
-        default:
-          description: unexpected error
-          content:
-            application/json:
-              schema:
-                $ref: "#/components/schemas/Error"
-components:
-  schemas:
-    Pet:
-      type: object
-      required:
-        - id
-        - name
-      properties:
-        id:
-          type: integer
-          format: int64
-        name:
-          type: string
-        tag:
-          type: string
-    Pets:
-      type: array
-      items:
-        $ref: "#/components/schemas/Pet"
-    Error:
-      type: object
-      required:
-        - code
-        - message
-      properties:
-        code:
-          type: integer
-          format: int32
-        message:
-          type: string
diff --git a/swagger2markup-builder/src/test/resources/logback.xml b/swagger2markup-builder/src/test/resources/logback.xml
index 8b43a9e7..23c3c8d0 100644
--- a/swagger2markup-builder/src/test/resources/logback.xml
+++ b/swagger2markup-builder/src/test/resources/logback.xml
@@ -6,7 +6,7 @@
         
     
 
-    
+    
         
     
-
\ No newline at end of file
+
diff --git a/swagger2markup-swagger-v2/build.gradle b/swagger2markup-swagger-v2/build.gradle
index 819c11cc..0dd269a7 100644
--- a/swagger2markup-swagger-v2/build.gradle
+++ b/swagger2markup-swagger-v2/build.gradle
@@ -29,7 +29,6 @@ dependencies {
     implementation implLibraries.slf4j
     implementation implLibraries.swaggerV2
     implementation implLibraries.vavr
-    testImplementation testLibraries.asciiDocJ
     testImplementation testLibraries.assertj
     testImplementation testLibraries.assertjDiff
     testImplementation testLibraries.junit
diff --git a/swagger2markup-swagger-v3/build.gradle b/swagger2markup-swagger-v3/build.gradle
index 87ba6804..b46df34e 100644
--- a/swagger2markup-swagger-v3/build.gradle
+++ b/swagger2markup-swagger-v3/build.gradle
@@ -15,7 +15,12 @@ dependencies {
         resolutionStrategy.force dependencyOverrides.findBugs
         resolutionStrategy.force dependencyOverrides.jaksonCore
     }
-    implementation implLibraries.swaggerV2Converter
+//    implementation implLibraries.swaggerV2Converter
+    compile project(':swagger2markup-asciidoc')
+    implementation implLibraries.asciiDocJApi
+    implementation implLibraries.commonsText
+    implementation implLibraries.slf4j
     implementation implLibraries.swaggerV3
     testImplementation testLibraries.junit
+    testImplementation testLibraries.logback
 }
diff --git a/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApi2AsciiDoc.java b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApi2AsciiDoc.java
new file mode 100644
index 00000000..475eebd6
--- /dev/null
+++ b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApi2AsciiDoc.java
@@ -0,0 +1,19 @@
+package io.github.swagger2markup;
+
+import io.github.swagger2markup.adoc.ast.impl.DocumentImpl;
+import io.swagger.v3.oas.models.OpenAPI;
+import org.asciidoctor.ast.Document;
+
+
+public class OpenApi2AsciiDoc {
+
+    public String translate(OpenAPI openAPI) {
+        Document rootDocument = new DocumentImpl();
+        OpenApiInfoSection.addInfoSection(rootDocument, openAPI);
+        OpenApiTagsSection.appendTagsSection(rootDocument, openAPI.getTags());
+        OpenApiServerSection.appendServersSection(rootDocument, openAPI.getServers());
+        OpenApiPathsSection.appendPathSection(rootDocument, openAPI.getPaths());
+        OpenApiComponentsSection.appendComponentsSection(rootDocument, openAPI.getComponents());
+        return rootDocument.convert();
+    }
+}
diff --git a/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiComponentsSection.java b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiComponentsSection.java
new file mode 100644
index 00000000..9f16d55b
--- /dev/null
+++ b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiComponentsSection.java
@@ -0,0 +1,62 @@
+package io.github.swagger2markup;
+
+import io.github.swagger2markup.adoc.ast.impl.SectionImpl;
+import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.media.Schema;
+import io.swagger.v3.oas.models.parameters.Parameter;
+import org.asciidoctor.ast.Document;
+import org.asciidoctor.ast.Section;
+
+import java.util.Map;
+
+import static io.github.swagger2markup.OpenApiHelpers.*;
+
+
+public class OpenApiComponentsSection {
+    public static void appendComponentsSection(Document document, Components components) {
+        if (null == components) return;
+
+        Section componentsSection = new SectionImpl(document);
+        componentsSection.setTitle(SECTION_TITLE_COMPONENTS);
+        String componentSectionId = "_components";
+        componentsSection.setId(componentSectionId);
+
+        appendComponentsSchemasSection(componentsSection, componentSectionId, components.getSchemas());
+        appendComponentsParameters(componentsSection, componentSectionId, components.getParameters());
+        appendResponses(componentsSection, components.getResponses());
+        appendHeadersTable(componentsSection, components.getHeaders());
+        generateLinksDocument(componentsSection, components.getLinks());
+
+        document.append(componentsSection);
+    }
+
+    private static void appendComponentsSchemasSection(
+            Section componentsSection, String componentSectionId,
+            @SuppressWarnings("rawtypes") Map schemas) {
+        if (null == schemas || schemas.isEmpty()) return;
+
+        SectionImpl schemasSection = new SectionImpl(componentsSection);
+        String schemasSectionId = componentSectionId + "_schemas";
+        schemasSection.setTitle(SECTION_TITLE_SCHEMAS);
+        schemasSection.setId(schemasSectionId);
+        schemas.forEach((name, schema) -> {
+            String schemaDocumentId = schemasSectionId + "_" + name;
+            Document schemaDocument = generateSchemaDocument(schemasSection, schema);
+            schemaDocument.setTitle(name);
+            schemaDocument.setId(schemaDocumentId);
+            schemasSection.append(schemaDocument);
+        });
+
+        componentsSection.append(schemasSection);
+    }
+
+    private static void appendComponentsParameters(Section componentsSection, String componentSectionId, Map parameters) {
+        if (null == parameters || parameters.isEmpty()) return;
+
+        SectionImpl parametersSection = new SectionImpl(componentsSection);
+        String parametersSectionId = componentSectionId + "_parameters";
+        parametersSection.setTitle(SECTION_TITLE_PARAMETERS);
+        parametersSection.setId(parametersSectionId);
+        appendParameters(parametersSection, parameters);
+    }
+}
diff --git a/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiHelpers.java b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiHelpers.java
new file mode 100644
index 00000000..15aa4a08
--- /dev/null
+++ b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiHelpers.java
@@ -0,0 +1,313 @@
+package io.github.swagger2markup;
+
+import io.github.swagger2markup.adoc.ast.impl.DocumentImpl;
+import io.github.swagger2markup.adoc.ast.impl.ParagraphBlockImpl;
+import io.github.swagger2markup.adoc.ast.impl.TableImpl;
+import io.swagger.v3.oas.models.ExternalDocumentation;
+import io.swagger.v3.oas.models.headers.Header;
+import io.swagger.v3.oas.models.links.Link;
+import io.swagger.v3.oas.models.media.Schema;
+import io.swagger.v3.oas.models.parameters.Parameter;
+import io.swagger.v3.oas.models.parameters.RequestBody;
+import io.swagger.v3.oas.models.responses.ApiResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.asciidoctor.ast.Block;
+import org.asciidoctor.ast.Document;
+import org.asciidoctor.ast.StructuralNode;
+import org.asciidoctor.ast.Table;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static io.github.swagger2markup.adoc.converter.internal.Delimiters.LINE_SEPARATOR;
+
+public class OpenApiHelpers {
+
+    public static final String LABEL_DEFAULT = "Default";
+    public static final String LABEL_DEPRECATED = "Deprecated";
+    public static final String LABEL_EXCLUSIVE_MAXIMUM = "Exclusive Maximum";
+    public static final String LABEL_EXCLUSIVE_MINIMUM = "Exclusive Minimum";
+    public static final String LABEL_FORMAT = "Format";
+    public static final String LABEL_MAXIMUM = "Maximum";
+    public static final String LABEL_MAX_ITEMS = "Maximum Items";
+    public static final String LABEL_MAX_LENGTH = "Maximum Length";
+    public static final String LABEL_MAX_PROPERTIES = "Maximum Properties";
+    public static final String LABEL_MINIMUM = "Minimum";
+    public static final String LABEL_MIN_ITEMS = "Minimum Items";
+    public static final String LABEL_MIN_LENGTH = "Minimum Length";
+    public static final String LABEL_MIN_PROPERTIES = "Minimum Properties";
+    public static final String LABEL_MULTIPLE_OF = "Multiple Of";
+    public static final String LABEL_NO_LINKS = "No Links";
+    public static final String LABEL_NULLABLE = "Nullable";
+    public static final String LABEL_OPERATION = "Operation";
+    public static final String LABEL_OPTIONAL = "Optional";
+    public static final String LABEL_PARAMETERS = "Parameters";
+    public static final String LABEL_READ_ONLY = "Read Only";
+    public static final String LABEL_REQUIRED = "Required";
+    public static final String LABEL_SERVER = "Server";
+    public static final String LABEL_TERMS_OF_SERVICE = "Terms Of Service";
+    public static final String LABEL_TITLE = "Title";
+    public static final String LABEL_TYPE = "Type";
+    public static final String LABEL_UNIQUE_ITEMS = "Unique Items";
+    public static final String LABEL_WRITE_ONLY = "Write Only";
+    public static final String SECTION_TITLE_COMPONENTS = "Components";
+    public static final String SECTION_TITLE_PARAMETERS = "Parameters";
+    public static final String SECTION_TITLE_PATHS = "Paths";
+    public static final String SECTION_TITLE_SCHEMAS = "Schemas";
+    public static final String SECTION_TITLE_SERVERS = "Servers";
+    public static final String SECTION_TITLE_OVERVIEW = "Overview";
+    public static final String SECTION_TITLE_TAGS = "Tags";
+    public static final String TABLE_HEADER_DEFAULT = "Default";
+    public static final String TABLE_HEADER_DESCRIPTION = "Description";
+    public static final String TABLE_HEADER_HTTP_CODE = "Code";
+    public static final String TABLE_HEADER_LINKS = "Links";
+    public static final String TABLE_HEADER_NAME = "Name";
+    public static final String TABLE_HEADER_POSSIBLE_VALUES = "Possible Values";
+    public static final String TABLE_HEADER_SCHEMA = "Schema";
+    public static final String TABLE_HEADER_TYPE = "Type";
+    public static final String TABLE_HEADER_VARIABLE = "Variable";
+    public static final String TABLE_TITLE_HEADERS = "Headers";
+    public static final String TABLE_TITLE_PARAMETERS = "Parameters";
+    public static final String TABLE_TITLE_PROPERTIES = "Properties";
+    public static final String TABLE_TITLE_RESPONSES = "Responses";
+    public static final String TABLE_TITLE_SERVER_VARIABLES = "Server Variables";
+
+    public static void appendDescription(StructuralNode node, String description) {
+        if (StringUtils.isNotBlank(description)) {
+            Block paragraph = new ParagraphBlockImpl(node);
+            paragraph.setSource(description);
+            node.append(paragraph);
+        }
+    }
+
+    public static Document generateLinksDocument(StructuralNode parent, java.util.Map links) {
+        DocumentImpl linksDocument = new DocumentImpl(parent);
+        ParagraphBlockImpl linkParagraph = new ParagraphBlockImpl(linksDocument);
+        if (null == links || links.isEmpty()) {
+            linkParagraph.setSource(LABEL_NO_LINKS);
+        } else {
+            StringBuilder sb = new StringBuilder();
+            links.forEach((name, link) -> {
+                sb.append(name).append(" +").append(LINE_SEPARATOR);
+                sb.append(italicUnconstrained(LABEL_OPERATION)).append(' ').append(italicUnconstrained(link.getOperationId())).append(" +").append(LINE_SEPARATOR);
+                Map parameters = link.getParameters();
+                if (null != parameters && !parameters.isEmpty()) {
+                    sb.append(italicUnconstrained(LABEL_PARAMETERS)).append(" {").append(" +").append(LINE_SEPARATOR);
+                    parameters.forEach((param, value) -> {
+                        sb.append('"').append(param).append("\": \"").append(value).append('"').append(" +").append(LINE_SEPARATOR);
+                    });
+                    sb.append('}').append(" +").append(LINE_SEPARATOR);
+                }
+            });
+            linkParagraph.setSource(sb.toString());
+        }
+        linksDocument.append(linkParagraph);
+        return linksDocument;
+    }
+
+    public static Document generateSchemaDocument(StructuralNode parent, @SuppressWarnings("rawtypes") Schema schema) {
+        Document schemaDocument = new DocumentImpl(parent);
+        if (null == schema) return schemaDocument;
+
+        appendDescription(schemaDocument, schema.getDescription());
+
+        Map schemasBooleanProperties = new HashMap() {{
+            put(LABEL_DEPRECATED, schema.getDeprecated());
+            put(LABEL_NULLABLE, schema.getNullable());
+            put(LABEL_READ_ONLY, schema.getReadOnly());
+            put(LABEL_WRITE_ONLY, schema.getWriteOnly());
+            put(LABEL_UNIQUE_ITEMS, schema.getUniqueItems());
+            put(LABEL_EXCLUSIVE_MAXIMUM, schema.getExclusiveMaximum());
+            put(LABEL_EXCLUSIVE_MINIMUM, schema.getExclusiveMinimum());
+        }};
+
+        Map schemasValueProperties = new HashMap() {{
+            put(LABEL_TITLE, schema.getTitle());
+            put(LABEL_TYPE, schema.getType());
+            put(LABEL_DEFAULT, schema.getDefault());
+            put(LABEL_FORMAT, schema.getFormat());
+            put(LABEL_MAXIMUM, schema.getMaximum());
+            put(LABEL_MINIMUM, schema.getMinimum());
+            put(LABEL_MAX_LENGTH, schema.getMaxLength());
+            put(LABEL_MIN_LENGTH, schema.getMinLength());
+            put(LABEL_MAX_ITEMS, schema.getMaxItems());
+            put(LABEL_MIN_ITEMS, schema.getMinItems());
+            put(LABEL_MAX_PROPERTIES, schema.getMaxProperties());
+            put(LABEL_MIN_PROPERTIES, schema.getMinProperties());
+            put(LABEL_MULTIPLE_OF, schema.getMultipleOf());
+        }};
+
+        Stream schemaBooleanStream = schemasBooleanProperties.entrySet().stream()
+                .filter(e -> null != e.getValue() && e.getValue())
+                .map(e -> italicUnconstrained(e.getKey().toLowerCase()));
+        Stream schemaValueStream = schemasValueProperties.entrySet().stream()
+                .filter(e -> null != e.getValue() && StringUtils.isNotBlank(e.getValue().toString()))
+                .map(e -> e.getKey().toLowerCase() + ": " + e.getValue());
+
+        ParagraphBlockImpl paragraphBlock = new ParagraphBlockImpl(schemaDocument);
+        String source = Stream.concat(schemaBooleanStream, schemaValueStream).collect(Collectors.joining(" +" + LINE_SEPARATOR));
+        String ref = schema.get$ref();
+        if (StringUtils.isNotBlank(ref)) {
+            String alt = ref.substring(ref.lastIndexOf('/') + 1);
+            String anchor = ref.replaceFirst("#", "").replaceAll("/", "_");
+            source += "<<" + anchor + "," + alt + ">>" + LINE_SEPARATOR;
+        }
+        paragraphBlock.setSource(source);
+
+        schemaDocument.append(paragraphBlock);
+        appendPropertiesTable(schemaDocument, schema.getProperties(), schema.getRequired());
+        return schemaDocument;
+    }
+
+    public static void appendPropertiesTable(StructuralNode parent, @SuppressWarnings("rawtypes") Map properties, List schemaRequired) {
+        if (null == properties || properties.isEmpty()) return;
+
+        List finalSchemaRequired = (null == schemaRequired) ? new ArrayList<>() : schemaRequired;
+
+        TableImpl propertiesTable = new TableImpl(parent, new HashMap<>(), new ArrayList<>());
+        propertiesTable.setOption("header");
+        propertiesTable.setAttribute("caption", "", true);
+        propertiesTable.setAttribute("cols", ".^4a,.^16a", true);
+        propertiesTable.setTitle(TABLE_TITLE_PROPERTIES);
+        propertiesTable.setHeaderRow(TABLE_HEADER_NAME, TABLE_HEADER_SCHEMA);
+
+        properties.forEach((name, schema) -> {
+            propertiesTable.addRow(
+                    generateInnerDoc(propertiesTable, name + LINE_SEPARATOR + requiredIndicator(finalSchemaRequired.contains(name))),
+                    generateSchemaDocument(propertiesTable, schema)
+            );
+        });
+        parent.append(propertiesTable);
+    }
+
+    public static void appendHeadersTable(StructuralNode node, Map headers) {
+        if (null == headers || headers.isEmpty()) return;
+
+        TableImpl responseHeadersTable = new TableImpl(node, new HashMap<>(), new ArrayList<>());
+        responseHeadersTable.setOption("header");
+        responseHeadersTable.setAttribute("caption", "", true);
+        responseHeadersTable.setAttribute("cols", ".^2a,.^14a,.^4a", true);
+        responseHeadersTable.setTitle(TABLE_TITLE_HEADERS);
+        responseHeadersTable.setHeaderRow(TABLE_HEADER_NAME, TABLE_HEADER_DESCRIPTION, TABLE_HEADER_SCHEMA);
+        headers.forEach((name, header) ->
+                responseHeadersTable.addRow(
+                        generateInnerDoc(responseHeadersTable, name),
+                        generateInnerDoc(responseHeadersTable, Optional.ofNullable(header.getDescription()).orElse("")),
+                        generateSchemaDocument(responseHeadersTable, header.getSchema())
+                ));
+        node.append(responseHeadersTable);
+    }
+
+    static void appendParameters(StructuralNode parent, Map parameters) {
+        if (null == parameters || parameters.isEmpty()) return;
+
+        TableImpl pathParametersTable = new TableImpl(parent, new HashMap<>(), new ArrayList<>());
+        pathParametersTable.setOption("header");
+        pathParametersTable.setAttribute("caption", "", true);
+        pathParametersTable.setAttribute("cols", ".^2a,.^3a,.^10a,.^5a", true);
+        pathParametersTable.setTitle(TABLE_TITLE_PARAMETERS);
+        pathParametersTable.setHeaderRow(TABLE_HEADER_TYPE, TABLE_HEADER_NAME, TABLE_HEADER_DESCRIPTION, TABLE_HEADER_SCHEMA);
+
+        parameters.forEach((alt, parameter) ->
+                pathParametersTable.addRow(
+                        generateInnerDoc(pathParametersTable, boldUnconstrained(parameter.getIn()), alt),
+                        getParameterNameDocument(pathParametersTable, parameter),
+                        generateInnerDoc(pathParametersTable, Optional.ofNullable(parameter.getDescription()).orElse("")),
+                        generateSchemaDocument(pathParametersTable, parameter.getSchema())
+                ));
+        parent.append(pathParametersTable);
+    }
+
+    static void appendParameters(StructuralNode node, List parameters) {
+        if (null == parameters || parameters.isEmpty()) return;
+
+        appendParameters(node, parameters.stream().collect(Collectors.toMap(Parameter::getName, parameter -> parameter)));
+    }
+
+    static void appendRequestBody(StructuralNode node, RequestBody requestBody){
+        if(null == requestBody) return;
+    }
+
+    static void appendResponses(StructuralNode serverSection, Map apiResponses) {
+        if (null == apiResponses || apiResponses.isEmpty()) return;
+        TableImpl pathResponsesTable = new TableImpl(serverSection, new HashMap<>(), new ArrayList<>());
+        pathResponsesTable.setOption("header");
+        pathResponsesTable.setAttribute("caption", "", true);
+        pathResponsesTable.setAttribute("cols", ".^2a,.^14a,.^4a", true);
+        pathResponsesTable.setTitle(TABLE_TITLE_RESPONSES);
+        pathResponsesTable.setHeaderRow(TABLE_HEADER_HTTP_CODE, TABLE_HEADER_DESCRIPTION, TABLE_HEADER_LINKS);
+
+        apiResponses.forEach((httpCode, apiResponse) -> {
+            pathResponsesTable.addRow(
+                    generateInnerDoc(pathResponsesTable, httpCode),
+                    getResponseDescriptionColumnDocument(pathResponsesTable, apiResponse),
+                    generateLinksDocument(pathResponsesTable, apiResponse.getLinks())
+            );
+        });
+        serverSection.append(pathResponsesTable);
+    }
+
+    static Document getResponseDescriptionColumnDocument(Table table, ApiResponse apiResponse) {
+        Document document = generateInnerDoc(table, Optional.ofNullable(apiResponse.getDescription()).orElse(""));
+        appendHeadersTable(document, apiResponse.getHeaders());
+        return document;
+    }
+
+    private static Document getParameterNameDocument(Table table, Parameter parameter) {
+        String documentContent = boldUnconstrained(parameter.getName()) + " +" + LINE_SEPARATOR + requiredIndicator(parameter.getRequired());
+        return generateInnerDoc(table, documentContent);
+    }
+
+    public static Document generateInnerDoc(Table table, String documentContent) {
+        return generateInnerDoc(table, documentContent, "");
+    }
+
+    public static Document generateInnerDoc(Table table, String documentContent, String id) {
+        Document innerDoc = new DocumentImpl(table);
+        if (StringUtils.isNotBlank(id)) {
+            innerDoc.setId(id);
+        }
+
+        Block paragraph = new ParagraphBlockImpl(innerDoc);
+        paragraph.setSource(documentContent);
+        innerDoc.append(paragraph);
+        return innerDoc;
+    }
+
+    public static String requiredIndicator(boolean isRequired) {
+        return italicUnconstrained(isRequired ? LABEL_REQUIRED : LABEL_OPTIONAL).toLowerCase();
+    }
+
+    public static void appendExternalDoc(StructuralNode node, ExternalDocumentation extDoc){
+        if (extDoc == null) return;
+
+        String url = extDoc.getUrl();
+        if (StringUtils.isNotBlank(url)) {
+            Block paragraph = new ParagraphBlockImpl(node);
+            String desc = extDoc.getDescription();
+            paragraph.setSource(url + (StringUtils.isNotBlank(desc) ? "[" + desc + "]" : ""));
+            node.append(paragraph);
+        }
+    }
+
+    public static String superScript(String str) {
+        return "^" + str + "^";
+    }
+
+    public static String subScript(String str) {
+        return "~" + str + "~";
+    }
+
+    public static String italicUnconstrained(String str) {
+        return "__" + str + "__";
+    }
+
+    public static String boldUnconstrained(String str) {
+        return "**" + str + "**";
+    }
+
+    public static String monospaced(String str) {
+        return "`" + str + "`";
+    }
+}
diff --git a/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiInfoSection.java b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiInfoSection.java
new file mode 100644
index 00000000..61792eb9
--- /dev/null
+++ b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiInfoSection.java
@@ -0,0 +1,93 @@
+package io.github.swagger2markup;
+
+import io.github.swagger2markup.adoc.ast.impl.BlockImpl;
+import io.github.swagger2markup.adoc.ast.impl.ParagraphBlockImpl;
+import io.github.swagger2markup.adoc.ast.impl.SectionImpl;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Contact;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.info.License;
+import org.apache.commons.lang3.StringUtils;
+import org.asciidoctor.ast.Block;
+import org.asciidoctor.ast.Document;
+import org.asciidoctor.ast.Section;
+
+import java.util.Optional;
+
+import static io.github.swagger2markup.OpenApiHelpers.*;
+
+public class OpenApiInfoSection {
+
+    public static void addInfoSection(Document rootDocument, OpenAPI openAPI) {
+        Info apiInfo = openAPI.getInfo();
+        rootDocument.setAttribute("openapi", openAPI.getOpenapi(), true);
+        addDocumentTitle(rootDocument, apiInfo);
+        addAuthorInfo(rootDocument, apiInfo);
+        addVersionInfo(rootDocument, apiInfo);
+        appendOverview(rootDocument, apiInfo);
+        appendExternalDoc(rootDocument, openAPI.getExternalDocs());
+    }
+
+    public static void addDocumentTitle(Document rootDocument, Info apiInfo) {
+        String title = apiInfo.getTitle();
+        if (StringUtils.isNotBlank(title)) {
+            rootDocument.setTitle(title);
+        }
+    }
+
+    public static void addVersionInfo(Document rootDocument, Info info) {
+        String version = info.getVersion();
+        if (StringUtils.isNotBlank(version)) {
+            rootDocument.setAttribute("revnumber", version, true);
+        }
+    }
+
+    public static void addAuthorInfo(Document rootDocument, Info info) {
+        Contact contact = info.getContact();
+        if (null != contact) {
+            String author = Optional.ofNullable(contact.getName()).orElse("");
+            String email = contact.getEmail();
+            if (StringUtils.isNotBlank(email)) {
+                rootDocument.setAttribute("email", email, true);
+            }
+            rootDocument.setAttribute("author", author, true);
+            rootDocument.setAttribute("authorcount", 1L, true);
+        }
+    }
+
+    public static void appendOverview(Document document, Info info) {
+        Section overviewDoc = new SectionImpl(document);
+        overviewDoc.setTitle(SECTION_TITLE_OVERVIEW);
+
+        appendDescription(overviewDoc, info.getDescription());
+        appendTermsOfServiceInfo(overviewDoc, info);
+        appendLicenseInfo(overviewDoc, info);
+        document.append(overviewDoc);
+    }
+
+    public static void appendLicenseInfo(Section overviewDoc, Info info) {
+        License license = info.getLicense();
+        if (null != license) {
+            StringBuilder sb = new StringBuilder();
+            if (StringUtils.isNotBlank(license.getUrl())) {
+                sb.append(license.getUrl()).append("[");
+            }
+            sb.append(license.getName());
+            if (StringUtils.isNotBlank(license.getUrl())) {
+                sb.append("]");
+            }
+            BlockImpl paragraph = new ParagraphBlockImpl(overviewDoc);
+            paragraph.setSource(sb.toString());
+            overviewDoc.append(paragraph);
+        }
+    }
+
+    public static void appendTermsOfServiceInfo(Section overviewDoc, Info info) {
+        String termsOfService = info.getTermsOfService();
+        if (StringUtils.isNotBlank(termsOfService)) {
+            Block paragraph = new ParagraphBlockImpl(overviewDoc);
+            paragraph.setSource(termsOfService + "[" + LABEL_TERMS_OF_SERVICE + "]");
+            overviewDoc.append(paragraph);
+        }
+    }
+}
diff --git a/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiPathsSection.java b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiPathsSection.java
new file mode 100644
index 00000000..987f5c68
--- /dev/null
+++ b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiPathsSection.java
@@ -0,0 +1,37 @@
+package io.github.swagger2markup;
+
+import io.github.swagger2markup.adoc.ast.impl.SectionImpl;
+import io.swagger.v3.oas.models.Paths;
+import org.asciidoctor.ast.Document;
+
+import java.util.Optional;
+
+import static io.github.swagger2markup.OpenApiHelpers.*;
+import static io.github.swagger2markup.OpenApiServerSection.appendServersSection;
+
+public class OpenApiPathsSection {
+
+    public static void appendPathSection(Document document, Paths apiPaths) {
+        if(null == apiPaths || apiPaths.isEmpty()) return;
+
+        SectionImpl allPathsSection = new SectionImpl(document);
+        allPathsSection.setTitle(SECTION_TITLE_PATHS);
+
+        apiPaths.forEach((name, pathItem) -> {
+            pathItem.readOperationsMap().forEach(((httpMethod, operation) -> {
+                SectionImpl operationSection = new SectionImpl(allPathsSection);
+                String summary = Optional.ofNullable(operation.getSummary()).orElse("");
+                operationSection.setTitle((italicUnconstrained(httpMethod.name().toUpperCase()) + " " + monospaced(name) + " " + summary).trim());
+                appendDescription(operationSection, operation.getDescription());
+                appendExternalDoc(operationSection, operation.getExternalDocs());
+                appendParameters(operationSection, operation.getParameters());
+                appendResponses(operationSection, operation.getResponses());
+                appendServersSection(operationSection, operation.getServers());
+                allPathsSection.append(operationSection);
+            }));
+
+        });
+
+        document.append(allPathsSection);
+    }
+}
diff --git a/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiServerSection.java b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiServerSection.java
new file mode 100644
index 00000000..c68d4ed6
--- /dev/null
+++ b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiServerSection.java
@@ -0,0 +1,52 @@
+package io.github.swagger2markup;
+
+import io.github.swagger2markup.adoc.ast.impl.SectionImpl;
+import io.github.swagger2markup.adoc.ast.impl.TableImpl;
+import io.swagger.v3.oas.models.servers.Server;
+import io.swagger.v3.oas.models.servers.ServerVariables;
+import org.asciidoctor.ast.Section;
+import org.asciidoctor.ast.StructuralNode;
+
+import java.util.*;
+
+import static io.github.swagger2markup.OpenApiHelpers.*;
+
+public class OpenApiServerSection {
+
+    public static void appendServersSection(StructuralNode node, List servers) {
+        if (null == servers || servers.isEmpty()) return;
+
+        Section serversSection = new SectionImpl(node);
+        serversSection.setTitle(SECTION_TITLE_SERVERS);
+
+        servers.forEach(server -> {
+            Section serverSection = new SectionImpl(serversSection);
+            serverSection.setTitle(italicUnconstrained(LABEL_SERVER) + ": " + server.getUrl());
+
+            appendDescription(serverSection, server.getDescription());
+            ServerVariables variables = server.getVariables();
+            appendVariables(serverSection, variables);
+            serversSection.append(serverSection);
+        });
+        node.append(serversSection);
+    }
+
+    private static void appendVariables(Section serverSection, ServerVariables variables) {
+        if (null == variables || variables.isEmpty()) return;
+
+        TableImpl serverVariables = new TableImpl(serverSection, new HashMap() {{
+                put("header-option", "");
+                put("cols", ".^2a,.^9a,.^3a,.^4a");
+            }}, new ArrayList<>());
+        serverVariables.setTitle(TABLE_TITLE_SERVER_VARIABLES);
+
+        serverVariables.setHeaderRow(TABLE_HEADER_VARIABLE, TABLE_HEADER_DESCRIPTION, TABLE_HEADER_POSSIBLE_VALUES, TABLE_HEADER_DEFAULT);
+
+        variables.forEach((name, variable) -> {
+                String possibleValues = String.join(", ", Optional.ofNullable(variable.getEnum()).orElse(Collections.singletonList("Any")));
+                serverVariables.addRow(name, Optional.ofNullable(variable.getDescription()).orElse(""), possibleValues, variable.getDefault());
+
+            });
+        serverSection.append(serverVariables);
+    }
+}
diff --git a/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiTagsSection.java b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiTagsSection.java
new file mode 100644
index 00000000..9592532a
--- /dev/null
+++ b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiTagsSection.java
@@ -0,0 +1,41 @@
+package io.github.swagger2markup;
+
+import io.github.swagger2markup.adoc.ast.impl.DescriptionListEntryImpl;
+import io.github.swagger2markup.adoc.ast.impl.DescriptionListImpl;
+import io.github.swagger2markup.adoc.ast.impl.ListItemImpl;
+import io.github.swagger2markup.adoc.ast.impl.SectionImpl;
+import io.swagger.v3.oas.models.tags.Tag;
+import org.apache.commons.lang3.StringUtils;
+import org.asciidoctor.ast.Document;
+import org.asciidoctor.ast.Section;
+
+import java.util.Collections;
+import java.util.List;
+
+import static io.github.swagger2markup.OpenApiHelpers.*;
+
+public class OpenApiTagsSection {
+
+    public static void appendTagsSection(Document document, List openAPITags) {
+        if (null == openAPITags || openAPITags.isEmpty()) return;
+
+        Section tagsSection = new SectionImpl(document);
+        tagsSection.setTitle(SECTION_TITLE_TAGS);
+
+        DescriptionListImpl tagsList = new DescriptionListImpl(tagsSection);
+        openAPITags.forEach(tag -> {
+            DescriptionListEntryImpl tagEntry = new DescriptionListEntryImpl(tagsList, Collections.singletonList(new ListItemImpl(tagsList, tag.getName())));
+            String description = tag.getDescription();
+            if(StringUtils.isNotBlank(description)){
+                ListItemImpl tagDesc = new ListItemImpl(tagEntry, "");
+                tagDesc.setSource(description);
+                appendExternalDoc(tagDesc, tag.getExternalDocs());
+                tagEntry.setDescription(tagDesc);
+            }
+            tagsList.addEntry(tagEntry);
+        });
+
+        tagsSection.append(tagsList);
+        document.append(tagsSection);
+    }
+}
diff --git a/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiV3Converter.java b/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiV3Converter.java
deleted file mode 100644
index 36e567a9..00000000
--- a/swagger2markup-swagger-v3/src/main/java/io/github/swagger2markup/OpenApiV3Converter.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package io.github.swagger2markup;
-
-import io.swagger.v3.oas.models.OpenAPI;
-import io.swagger.v3.parser.OpenAPIV3Parser;
-
-public class OpenApiV3Converter {
-
-    public static void main(String[] args) {
-        OpenAPI openAPI = new OpenAPIV3Parser().read("/Users/austek/Workspace/swagger2markup/swagger2markup-swagger-v2/src/test/resources/json/swagger_polymorphism.json");
-        openAPI.getInfo();
-//new DocumentImpl()
-        System.out.println(openAPI.toString());
-    }
-}
diff --git a/swagger2markup-swagger-v3/src/test/java/io/github/swagger2markup/OpenApi2AsciiDocTest.java b/swagger2markup-swagger-v3/src/test/java/io/github/swagger2markup/OpenApi2AsciiDocTest.java
new file mode 100644
index 00000000..e4a540e5
--- /dev/null
+++ b/swagger2markup-swagger-v3/src/test/java/io/github/swagger2markup/OpenApi2AsciiDocTest.java
@@ -0,0 +1,51 @@
+package io.github.swagger2markup;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.parser.OpenAPIV3Parser;
+import io.swagger.v3.parser.core.models.ParseOptions;
+import io.swagger.v3.parser.core.models.SwaggerParseResult;
+import org.apache.commons.io.IOUtils;
+import org.asciidoctor.Asciidoctor;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@RunWith(Parameterized.class)
+public class OpenApi2AsciiDocTest {
+
+    private OpenApi2AsciiDoc openApi2AsciiDoc = new OpenApi2AsciiDoc();
+    final private String openApiFile;
+    final private String expectedAsciiDoc;
+
+    public OpenApi2AsciiDocTest(String openApiFile, String expectedAsciiDoc) throws IOException {
+        this.openApiFile = "./src/test/resources/open_api/" + openApiFile;
+        this.expectedAsciiDoc = IOUtils.toString(getClass().getResourceAsStream("/asciidoc/" + expectedAsciiDoc), StandardCharsets.UTF_8);
+    }
+
+    @Parameterized.Parameters(name = "Run {index}: open api={0}, asciidoc={1}")
+    public static Iterable data() {
+        return Arrays.asList(new Object[][]{
+                {"simple.yaml", "simple.adoc"},
+                {"petstore.yaml", "petstore.adoc"}
+        });
+    }
+
+
+    @Test
+    public void converts_open_api_v3_to_asciidoc() {
+        ParseOptions options = new ParseOptions();
+        options.setResolve(true);
+        SwaggerParseResult result = new OpenAPIV3Parser().readLocation(openApiFile, null, options);
+        OpenAPI swagger = result.getOpenAPI();
+        assertNotNull(swagger);
+
+        assertEquals(expectedAsciiDoc, openApi2AsciiDoc.translate(swagger));
+    }
+}
diff --git a/swagger2markup-swagger-v3/src/test/java/io/github/swagger2markup/OpenApiV3ConverterTest.java b/swagger2markup-swagger-v3/src/test/java/io/github/swagger2markup/OpenApiV3ConverterTest.java
deleted file mode 100644
index c087ef6e..00000000
--- a/swagger2markup-swagger-v3/src/test/java/io/github/swagger2markup/OpenApiV3ConverterTest.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.github.swagger2markup;
-
-import org.junit.Test;
-
-public class OpenApiV3ConverterTest {
-
-    @Test
-    public void name() {
-//        OpenAPI openAPI = new OpenAPIV3Parser().read("/Users/austek/Workspace/swagger2markup/swagger2markup-swagger-v2/src/test/resources/json/swagger_polymorphism.json");
-//        openAPI.getInfo();
-    }
-}
diff --git a/swagger2markup-swagger-v3/src/test/resources/asciidoc/petstore.adoc b/swagger2markup-swagger-v3/src/test/resources/asciidoc/petstore.adoc
new file mode 100644
index 00000000..67b6ccbc
--- /dev/null
+++ b/swagger2markup-swagger-v3/src/test/resources/asciidoc/petstore.adoc
@@ -0,0 +1,1456 @@
+= Swagger Petstore
+apiteam@swagger.io
+v1.0.0
+:revnumber: 1.0.0
+:openapi: 3.0.0
+:author: apiteam@swagger.io
+:authorcount: 1
+
+== Overview
+This is a sample server Petstore server.  You can find out more about Swagger at http://swagger.io or on irc.freenode.net, #swagger.  For this sample, you can use the api key "special-key" to test the authorization filters
+
+http://swagger.io/terms/[Terms Of Service]
+
+http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0]
+
+https://swagger.io[Find more info here]
+
+== Tags
+pet::
+Pet Operations
++
+http://swagger.io
+
+user::
+All about the Users
+
+== Servers
+=== __Server__: http://localhost:8000/v2/api
+
+== Paths
+=== __POST__ `/pet/add` Add a new pet to the store
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|405
+
+
+<.<|Invalid input
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __PUT__ `/pet` Update an existing pet
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|400
+
+
+<.<|Invalid ID supplied
+
+
+<.<|No Links
+
+
+
+<.<|404
+
+
+<.<|Pet not found
+
+
+<.<|No Links
+
+
+
+<.<|405
+
+
+<.<|Validation exception
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __POST__ `/pet` Add a new pet to the store
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|405
+
+
+<.<|Invalid input
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __GET__ `/pet/findByStatus` Finds Pets by status
+Multiple status values can be provided with comma seperated strings
+
+.Parameters
+[%header,caption=,cols=".^2a,.^3a,.^10a,.^5a"]
+|===
+<.<|Type
+
+
+<.<|Name
+
+
+<.<|Description
+
+
+<.<|Schema
+
+
+
+<.<|**query**
+
+
+<.<|**status** +
+__optional__
+
+
+<.<|Status values that need to be considered for filter
+
+
+<.<|type: array
+
+
+
+|===
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|200
+
+
+<.<|successful operation
+
+
+<.<|No Links
+
+
+
+<.<|400
+
+
+<.<|Invalid status value
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __GET__ `/pet/findByTags` Finds Pets by tags
+Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.
+
+.Parameters
+[%header,caption=,cols=".^2a,.^3a,.^10a,.^5a"]
+|===
+<.<|Type
+
+
+<.<|Name
+
+
+<.<|Description
+
+
+<.<|Schema
+
+
+
+<.<|**query**
+
+
+<.<|**tags** +
+__optional__
+
+
+<.<|Tags to filter by
+
+
+<.<|type: array
+
+
+
+|===
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|200
+
+
+<.<|successful operation
+
+
+<.<|No Links
+
+
+
+<.<|400
+
+
+<.<|Invalid tag value
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __GET__ `/pet/{petId}` Find pet by ID
+Returns a pet when ID < 10.  ID > 10 or nonintegers will simulate API error conditions
+
+.Parameters
+[%header,caption=,cols=".^2a,.^3a,.^10a,.^5a"]
+|===
+<.<|Type
+
+
+<.<|Name
+
+
+<.<|Description
+
+
+<.<|Schema
+
+
+
+<.<|**path**
+
+
+<.<|**petId** +
+__required__
+
+
+<.<|ID of pet that needs to be fetched
+
+
+<.<|type: integer +
+format: int64
+
+
+
+|===
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|200
+
+
+<.<|successful operation
+
+
+<.<|No Links
+
+
+
+<.<|400
+
+
+<.<|Invalid ID supplied
+
+
+<.<|No Links
+
+
+
+<.<|404
+
+
+<.<|Pet not found
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __POST__ `/pet/{petId}` Updates a pet in the store with form data
+.Parameters
+[%header,caption=,cols=".^2a,.^3a,.^10a,.^5a"]
+|===
+<.<|Type
+
+
+<.<|Name
+
+
+<.<|Description
+
+
+<.<|Schema
+
+
+
+<.<|**path**
+
+
+<.<|**petId** +
+__required__
+
+
+<.<|ID of pet that needs to be updated
+
+
+<.<|type: string
+
+
+
+|===
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|405
+
+
+<.<|Invalid input
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __DELETE__ `/pet/{petId}` Deletes a pet
+.Parameters
+[%header,caption=,cols=".^2a,.^3a,.^10a,.^5a"]
+|===
+<.<|Type
+
+
+<.<|Name
+
+
+<.<|Description
+
+
+<.<|Schema
+
+
+
+<.<|**path**
+
+
+<.<|**petId** +
+__required__
+
+
+<.<|Pet id to delete
+
+
+<.<|type: integer +
+format: int64
+
+
+
+<.<|**header**
+
+
+<.<|**api_key** +
+__optional__
+
+
+<.<|
+<.<|type: string
+
+
+
+|===
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|400
+
+
+<.<|Invalid pet value
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __POST__ `/pet/{petId}/uploadImage` uploads an image
+.Parameters
+[%header,caption=,cols=".^2a,.^3a,.^10a,.^5a"]
+|===
+<.<|Type
+
+
+<.<|Name
+
+
+<.<|Description
+
+
+<.<|Schema
+
+
+
+<.<|**path**
+
+
+<.<|**petId** +
+__required__
+
+
+<.<|ID of pet to update
+
+
+<.<|type: integer +
+format: int64
+
+
+
+|===
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|200
+
+
+<.<|successful operation
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __GET__ `/store/inventory` Returns pet inventories by status
+Returns a map of status codes to quantities
+
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|200
+
+
+<.<|successful operation
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __POST__ `/store/order` Place an order for a pet
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|200
+
+
+<.<|successful operation
+
+
+<.<|No Links
+
+
+
+<.<|400
+
+
+<.<|Invalid Order
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __GET__ `/store/order/{orderId}` Find purchase order by ID
+For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions
+
+.Parameters
+[%header,caption=,cols=".^2a,.^3a,.^10a,.^5a"]
+|===
+<.<|Type
+
+
+<.<|Name
+
+
+<.<|Description
+
+
+<.<|Schema
+
+
+
+<.<|**path**
+
+
+<.<|**orderId** +
+__required__
+
+
+<.<|ID of pet that needs to be fetched
+
+
+<.<|type: string
+
+
+
+|===
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|200
+
+
+<.<|successful operation
+
+
+<.<|No Links
+
+
+
+<.<|400
+
+
+<.<|Invalid ID supplied
+
+
+<.<|No Links
+
+
+
+<.<|404
+
+
+<.<|Order not found
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __DELETE__ `/store/order/{orderId}` Delete purchase order by ID
+For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
+
+.Parameters
+[%header,caption=,cols=".^2a,.^3a,.^10a,.^5a"]
+|===
+<.<|Type
+
+
+<.<|Name
+
+
+<.<|Description
+
+
+<.<|Schema
+
+
+
+<.<|**path**
+
+
+<.<|**orderId** +
+__required__
+
+
+<.<|ID of the order that needs to be deleted
+
+
+<.<|type: string
+
+
+
+|===
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|400
+
+
+<.<|Invalid ID supplied
+
+
+<.<|No Links
+
+
+
+<.<|404
+
+
+<.<|Order not found
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __POST__ `/user` Create user
+This can only be done by the logged in user.
+
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|default
+
+
+<.<|successful operation
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __POST__ `/user/createWithArray` Creates list of users with given input array
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|default
+
+
+<.<|successful operation
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __POST__ `/user/createWithList` Creates list of users with given input array
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|default
+
+
+<.<|successful operation
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __GET__ `/user/login` Logs user into the system
+.Parameters
+[%header,caption=,cols=".^2a,.^3a,.^10a,.^5a"]
+|===
+<.<|Type
+
+
+<.<|Name
+
+
+<.<|Description
+
+
+<.<|Schema
+
+
+
+<.<|**query**
+
+
+<.<|**password** +
+__optional__
+
+
+<.<|The password for login in clear text
+
+
+<.<|type: string
+
+
+
+<.<|**query**
+
+
+<.<|**username** +
+__optional__
+
+
+<.<|The user name for login
+
+
+<.<|type: string
+
+
+
+|===
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|200
+
+
+<.<|successful operation
+
+
+<.<|No Links
+
+
+
+<.<|400
+
+
+<.<|Invalid username/password supplied
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __GET__ `/user/logout` Logs out current logged in user session
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|default
+
+
+<.<|successful operation
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __GET__ `/user/{username}` Get user by user name
+.Parameters
+[%header,caption=,cols=".^2a,.^3a,.^10a,.^5a"]
+|===
+<.<|Type
+
+
+<.<|Name
+
+
+<.<|Description
+
+
+<.<|Schema
+
+
+
+<.<|**path**
+
+
+<.<|**username** +
+__required__
+
+
+<.<|The name that needs to be fetched. Use user1 for testing.
+
+
+<.<|type: string
+
+
+
+|===
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|200
+
+
+<.<|successful operation
+
+
+<.<|No Links
+
+
+
+<.<|400
+
+
+<.<|Invalid username supplied
+
+
+<.<|No Links
+
+
+
+<.<|404
+
+
+<.<|User not found
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __PUT__ `/user/{username}` Updated user
+This can only be done by the logged in user.
+
+.Parameters
+[%header,caption=,cols=".^2a,.^3a,.^10a,.^5a"]
+|===
+<.<|Type
+
+
+<.<|Name
+
+
+<.<|Description
+
+
+<.<|Schema
+
+
+
+<.<|**path**
+
+
+<.<|**username** +
+__required__
+
+
+<.<|name that need to be deleted
+
+
+<.<|type: string
+
+
+
+|===
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|400
+
+
+<.<|Invalid user supplied
+
+
+<.<|No Links
+
+
+
+<.<|404
+
+
+<.<|User not found
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __DELETE__ `/user/{username}` Delete user
+This can only be done by the logged in user.
+
+.Parameters
+[%header,caption=,cols=".^2a,.^3a,.^10a,.^5a"]
+|===
+<.<|Type
+
+
+<.<|Name
+
+
+<.<|Description
+
+
+<.<|Schema
+
+
+
+<.<|**path**
+
+
+<.<|**username** +
+__required__
+
+
+<.<|The name that needs to be deleted
+
+
+<.<|type: string
+
+
+
+|===
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|400
+
+
+<.<|Invalid username supplied
+
+
+<.<|No Links
+
+
+
+<.<|404
+
+
+<.<|User not found
+
+
+<.<|No Links
+
+
+
+|===
+
+[[_components]]
+== Components
+[[_components_schemas]]
+=== Schemas
+[[_components_schemas_user]]
+==== User
+
+.Properties
+[%header,caption=,cols=".^4a,.^16a"]
+|===
+<.<|Name
+
+
+<.<|Schema
+
+
+
+<.<|id
+__optional__
+
+
+<.<|type: integer +
+format: int64
+
+
+
+<.<|username
+__optional__
+
+
+<.<|type: string
+
+
+
+<.<|firstName
+__optional__
+
+
+<.<|type: string
+
+
+
+<.<|lastName
+__optional__
+
+
+<.<|type: string
+
+
+
+<.<|email
+__optional__
+
+
+<.<|type: string
+
+
+
+<.<|password
+__optional__
+
+
+<.<|type: string
+
+
+
+<.<|phone
+__optional__
+
+
+<.<|type: string
+
+
+
+<.<|userStatus
+__optional__
+
+
+<.<|User Status
+
+type: integer +
+format: int32
+
+
+
+|===
+[[_components_schemas_category]]
+==== Category
+
+.Properties
+[%header,caption=,cols=".^4a,.^16a"]
+|===
+<.<|Name
+
+
+<.<|Schema
+
+
+
+<.<|id
+__optional__
+
+
+<.<|type: integer +
+format: int64
+
+
+
+<.<|name
+__optional__
+
+
+<.<|type: string
+
+
+
+|===
+[[_components_schemas_pet]]
+==== Pet
+
+.Properties
+[%header,caption=,cols=".^4a,.^16a"]
+|===
+<.<|Name
+
+
+<.<|Schema
+
+
+
+<.<|id
+__optional__
+
+
+<.<|type: integer +
+format: int64
+
+
+
+<.<|category
+__optional__
+
+
+<.<|<<_components_schemas_Category,Category>>
+
+
+
+<.<|name
+__required__
+
+
+<.<|type: string
+
+
+
+<.<|photoUrls
+__required__
+
+
+<.<|type: array
+
+
+
+<.<|tags
+__optional__
+
+
+<.<|type: array
+
+
+
+<.<|status
+__optional__
+
+
+<.<|pet status in the store
+
+type: string
+
+
+
+|===
+[[_components_schemas_tag]]
+==== Tag
+
+.Properties
+[%header,caption=,cols=".^4a,.^16a"]
+|===
+<.<|Name
+
+
+<.<|Schema
+
+
+
+<.<|id
+__optional__
+
+
+<.<|type: integer +
+format: int64
+
+
+
+<.<|name
+__optional__
+
+
+<.<|type: string
+
+
+
+|===
+[[_components_schemas_order]]
+==== Order
+
+.Properties
+[%header,caption=,cols=".^4a,.^16a"]
+|===
+<.<|Name
+
+
+<.<|Schema
+
+
+
+<.<|id
+__optional__
+
+
+<.<|type: integer +
+format: int64
+
+
+
+<.<|petId
+__optional__
+
+
+<.<|type: integer +
+format: int64
+
+
+
+<.<|quantity
+__optional__
+
+
+<.<|type: integer +
+format: int32
+
+
+
+<.<|shipDate
+__optional__
+
+
+<.<|type: string +
+format: date-time
+
+
+
+<.<|status
+__optional__
+
+
+<.<|Order Status
+
+type: string
+
+
+
+<.<|complete
+__optional__
+
+
+<.<|type: boolean
+
+
+
+|===
+[[_components_schemas_petarray]]
+==== PetArray
+
+type: array
+
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|veryBad
+
+
+<.<|failed
+
+
+<.<|No Links
+
+
+
+|===
+
diff --git a/swagger2markup-swagger-v3/src/test/resources/asciidoc/simple.adoc b/swagger2markup-swagger-v3/src/test/resources/asciidoc/simple.adoc
new file mode 100644
index 00000000..2bcc7994
--- /dev/null
+++ b/swagger2markup-swagger-v3/src/test/resources/asciidoc/simple.adoc
@@ -0,0 +1,270 @@
+= Simple Inventory API
+
+v1.0.0
+:revnumber: 1.0.0
+:openapi: 3.0.0
+:authorcount: 1
+:email: you@your-company.com
+
+== Overview
+This is a simple API
+
+http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0]
+
+== Tags
+admins::
+Secured Admin-only calls
+developers::
+Operations available to regular developers
+
+== Servers
+=== __Server__: /
+
+== Paths
+=== __GET__ `/inventory` searches inventory
+By passing in the appropriate options, you can search for
+available inventory in the system
+
+.Parameters
+[%header,caption=,cols=".^2a,.^3a,.^10a,.^5a"]
+|===
+<.<|Type
+
+
+<.<|Name
+
+
+<.<|Description
+
+
+<.<|Schema
+
+
+
+<.<|**query**
+
+
+<.<|**searchString** +
+__optional__
+
+
+<.<|pass an optional search string for looking up inventory
+
+
+<.<|type: string
+
+
+
+<.<|**query**
+
+
+<.<|**limit** +
+__optional__
+
+
+<.<|maximum number of records to return
+
+
+<.<|minimum: 0 +
+type: integer +
+maximum: 50 +
+format: int32
+
+
+
+<.<|**query**
+
+
+<.<|**skip** +
+__optional__
+
+
+<.<|number of records to skip for pagination
+
+
+<.<|minimum: 0 +
+type: integer +
+format: int32
+
+
+
+|===
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|200
+
+
+<.<|search results matching criteria
+
+
+<.<|No Links
+
+
+
+<.<|400
+
+
+<.<|bad input parameter
+
+
+<.<|No Links
+
+
+
+|===
+
+=== __POST__ `/inventory` adds an inventory item
+Adds an item to the system
+
+.Responses
+[%header,caption=,cols=".^2a,.^14a,.^4a"]
+|===
+<.<|Code
+
+
+<.<|Description
+
+
+<.<|Links
+
+
+
+<.<|201
+
+
+<.<|item created
+
+
+<.<|No Links
+
+
+
+<.<|400
+
+
+<.<|invalid input, object invalid
+
+
+<.<|No Links
+
+
+
+<.<|409
+
+
+<.<|an existing item already exists
+
+
+<.<|No Links
+
+
+
+|===
+
+[[_components]]
+== Components
+[[_components_schemas]]
+=== Schemas
+[[_components_schemas_inventoryitem]]
+==== InventoryItem
+
+type: object
+
+.Properties
+[%header,caption=,cols=".^4a,.^16a"]
+|===
+<.<|Name
+
+
+<.<|Schema
+
+
+
+<.<|id
+__required__
+
+
+<.<|type: string +
+format: uuid
+
+
+
+<.<|name
+__required__
+
+
+<.<|type: string
+
+
+
+<.<|releaseDate
+__required__
+
+
+<.<|type: string +
+format: date-time
+
+
+
+<.<|manufacturer
+__required__
+
+
+<.<|<<_components_schemas_Manufacturer,Manufacturer>>
+
+
+
+|===
+[[_components_schemas_manufacturer]]
+==== Manufacturer
+
+type: object
+
+.Properties
+[%header,caption=,cols=".^4a,.^16a"]
+|===
+<.<|Name
+
+
+<.<|Schema
+
+
+
+<.<|name
+__required__
+
+
+<.<|type: string
+
+
+
+<.<|homePage
+__optional__
+
+
+<.<|type: string +
+format: url
+
+
+
+<.<|phone
+__optional__
+
+
+<.<|type: string
+
+
+
+|===
+
diff --git a/swagger2markup-swagger-v3/src/test/resources/logback.xml b/swagger2markup-swagger-v3/src/test/resources/logback.xml
new file mode 100644
index 00000000..5666f523
--- /dev/null
+++ b/swagger2markup-swagger-v3/src/test/resources/logback.xml
@@ -0,0 +1,14 @@
+
+
+    
+        
+            %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+        
+    
+
+    
+
+    
+        
+    
+
diff --git a/swagger2markup-swagger-v3/src/test/resources/open_api/petstore.yaml b/swagger2markup-swagger-v3/src/test/resources/open_api/petstore.yaml
new file mode 100644
index 00000000..dac0e994
--- /dev/null
+++ b/swagger2markup-swagger-v3/src/test/resources/open_api/petstore.yaml
@@ -0,0 +1,709 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://localhost:8000/v2/api'
+info:
+  description: >-
+    This is a sample server Petstore server.  You can find out more about
+    Swagger at http://swagger.io or on
+    irc.freenode.net, #swagger.  For this sample, you can use the api key
+    "special-key" to test the authorization filters
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    name: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+externalDocs:
+  description: Find more info here
+  url: 'https://swagger.io'
+tags:
+  - name: pet
+    description: Pet Operations
+    externalDocs:
+      url: 'http://swagger.io'
+  - name: user
+    description: All about the Users
+paths:
+  /pet/add:
+    post:
+      tags:
+        - pet
+      summary: Add a new pet to the store
+      description: ''
+      operationId: createPet
+      responses:
+        '405':
+          description: Invalid input
+      security:
+        - petstore_auth:
+            - 'write:pets'
+            - 'read:pets'
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/Pet'
+        description: Pet object that needs to be added to the store
+  /pet:
+    post:
+      tags:
+        - pet
+      summary: Add a new pet to the store
+      description: ''
+      operationId: addPet
+      responses:
+        '405':
+          description: Invalid input
+      security:
+        - petstore_auth:
+            - 'write:pets'
+            - 'read:pets'
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/Pet'
+          application/xml:
+            schema:
+              $ref: '#/components/schemas/Pet'
+        description: Pet object that needs to be added to the store
+    put:
+      tags:
+        - pet
+      summary: Update an existing pet
+      description: ''
+      operationId: updatePet
+      responses:
+        '400':
+          description: Invalid ID supplied
+        '404':
+          description: Pet not found
+        '405':
+          description: Validation exception
+      security:
+        - petstore_auth:
+            - 'write:pets'
+            - 'read:pets'
+      requestBody:
+        $ref: '#/components/requestBodies/Pet'
+  /pet/findByStatus:
+    get:
+      tags:
+        - pet
+      summary: Finds Pets by status
+      description: Multiple status values can be provided with comma seperated strings
+      operationId: findPetsByStatus
+      parameters:
+        - name: status
+          in: query
+          description: Status values that need to be considered for filter
+          required: false
+          style: pipeDelimited
+          schema:
+            type: array
+            items:
+              type: string
+            default: available
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/PetArray'
+            application/xml:
+              schema:
+                $ref: '#/components/schemas/PetArray'
+        '400':
+          description: Invalid status value
+      security:
+        - petstore_auth:
+            - 'write:pets'
+            - 'read:pets'
+  /pet/findByTags:
+    get:
+      tags:
+        - pet
+      summary: Finds Pets by tags
+      description: >-
+        Muliple tags can be provided with comma seperated strings. Use tag1,
+        tag2, tag3 for testing.
+      operationId: findPetsByTags
+      parameters:
+        - name: tags
+          in: query
+          description: Tags to filter by
+          required: false
+          explode: true
+          schema:
+            type: array
+            items:
+              type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/Pet'
+            application/xml:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/Pet'
+        '400':
+          description: Invalid tag value
+      security:
+        - petstore_auth:
+            - 'write:pets'
+            - 'read:pets'
+  '/pet/{petId}':
+    get:
+      tags:
+        - pet
+      summary: Find pet by ID
+      description: >-
+        Returns a pet when ID < 10.  ID > 10 or nonintegers will simulate API
+        error conditions
+      operationId: getPetById
+      parameters:
+        - name: petId
+          in: path
+          description: ID of pet that needs to be fetched
+          required: true
+          schema:
+            type: integer
+            format: int64
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Pet'
+            application/xml:
+              schema:
+                $ref: '#/components/schemas/Pet'
+        '400':
+          description: Invalid ID supplied
+        '404':
+          description: Pet not found
+      security:
+        - api_key: []
+        - petstore_auth:
+            - 'write:pets'
+            - 'read:pets'
+    post:
+      tags:
+        - pet
+      summary: Updates a pet in the store with form data
+      description: ''
+      operationId: updatePetWithForm
+      parameters:
+        - name: petId
+          in: path
+          description: ID of pet that needs to be updated
+          required: true
+          schema:
+            type: string
+      responses:
+        '405':
+          description: Invalid input
+      security:
+        - petstore_auth:
+            - 'write:pets'
+            - 'read:pets'
+      requestBody:
+        content:
+          application/x-www-form-urlencoded:
+            schema:
+              type: object
+              properties:
+                name:
+                  description: Updated name of the pet
+                  type: string
+                status:
+                  description: Updated status of the pet
+                  type: string
+    delete:
+      tags:
+        - pet
+      summary: Deletes a pet
+      description: ''
+      operationId: deletePet
+      parameters:
+        - name: api_key
+          in: header
+          description: ''
+          required: false
+          schema:
+            type: string
+        - name: petId
+          in: path
+          description: Pet id to delete
+          required: true
+          schema:
+            type: integer
+            format: int64
+      responses:
+        '400':
+          description: Invalid pet value
+      security:
+        - petstore_auth:
+            - 'write:pets'
+            - 'read:pets'
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/Pet'
+        description: Pet extra params that needs to be deleted
+  '/pet/{petId}/uploadImage':
+    post:
+      tags:
+        - pet
+      summary: uploads an image
+      description: ''
+      operationId: uploadFile
+      parameters:
+        - name: petId
+          in: path
+          description: ID of pet to update
+          required: true
+          schema:
+            type: integer
+            format: int64
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/ApiResponse"
+      security:
+        - petstore_auth:
+            - 'write:pets'
+            - 'read:pets'
+      requestBody:
+        content:
+          multipart/form-data:
+            schema:
+              type: object
+              properties:
+                additionalMetadata:
+                  description: Additional data to pass to server
+                  type: string
+                file:
+                  description: file to upload
+                  type: string
+                  format: binary
+  /store/inventory:
+    get:
+      tags:
+        - store
+      summary: Returns pet inventories by status
+      description: Returns a map of status codes to quantities
+      operationId: get inventory+1
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                additionalProperties:
+                  type: integer
+                  format: int32
+            application/xml:
+              schema:
+                type: object
+                additionalProperties:
+                  type: integer
+                  format: int32
+      security:
+        - api_key: []
+  /store/order:
+    post:
+      tags:
+        - store
+      summary: Place an order for a pet
+      description: ''
+      operationId: placeOrder
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Order'
+            application/xml:
+              schema:
+                $ref: '#/components/schemas/Order'
+        '400':
+          description: Invalid Order
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/Order'
+        description: order placed for purchasing the pet
+  '/store/order/{orderId}':
+    get:
+      tags:
+        - store
+      summary: Find purchase order by ID
+      description: >-
+        For valid response try integer IDs with value <= 5 or > 10. Other values
+        will generated exceptions
+      operationId: getOrderById
+      parameters:
+        - name: orderId
+          in: path
+          description: ID of pet that needs to be fetched
+          required: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Order'
+            application/xml:
+              schema:
+                $ref: '#/components/schemas/Order'
+        '400':
+          description: Invalid ID supplied
+        '404':
+          description: Order not found
+    delete:
+      tags:
+        - store
+      summary: Delete purchase order by ID
+      description: >-
+        For valid response try integer IDs with value < 1000. Anything above
+        1000 or nonintegers will generate API errors
+      operationId: deleteOrder
+      parameters:
+        - name: orderId
+          in: path
+          description: ID of the order that needs to be deleted
+          required: true
+          schema:
+            type: string
+      responses:
+        '400':
+          description: Invalid ID supplied
+        '404':
+          description: Order not found
+  /user:
+    post:
+      tags:
+        - user
+      summary: Create user
+      description: This can only be done by the logged in user.
+      operationId: createUser
+      responses:
+        default:
+          description: successful operation
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/User'
+        description: Created user object
+  /user/createWithArray:
+    post:
+      tags:
+        - user
+      summary: Creates list of users with given input array
+      description: ''
+      operationId: createUsersWithArrayInput
+      responses:
+        default:
+          description: successful operation
+      requestBody:
+        $ref: '#/components/requestBodies/UserArray'
+  /user/createWithList:
+    post:
+      tags:
+        - user
+      summary: Creates list of users with given input array
+      description: ''
+      operationId: createUsersWithListInput
+      responses:
+        default:
+          description: successful operation
+      requestBody:
+        $ref: '#/components/requestBodies/UserArray'
+  /user/login:
+    get:
+      tags:
+        - user
+      security:
+        - https
+      summary: Logs user into the system
+      description: ''
+      operationId: loginUser
+      parameters:
+        - name: username
+          in: query
+          description: The user name for login
+          required: false
+          schema:
+            type: string
+        - name: password
+          in: query
+          description: The password for login in clear text
+          required: false
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+            application/xml:
+              schema:
+                type: string
+        '400':
+          description: Invalid username/password supplied
+  /user/logout:
+    get:
+      tags:
+        - user
+      security:
+        - https
+      summary: Logs out current logged in user session
+      description: ''
+      operationId: logoutUser
+      responses:
+        default:
+          description: successful operation
+  '/user/{username}':
+    get:
+      tags:
+        - user
+      summary: Get user by user name
+      description: ''
+      operationId: getUserByName
+      parameters:
+        - name: username
+          in: path
+          description: 'The name that needs to be fetched. Use user1 for testing.'
+          required: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/User'
+            application/xml:
+              schema:
+                $ref: '#/components/schemas/User'
+        '400':
+          description: Invalid username supplied
+        '404':
+          description: User not found
+    put:
+      tags:
+        - user
+      summary: Updated user
+      description: This can only be done by the logged in user.
+      operationId: updateUser
+      parameters:
+        - name: username
+          in: path
+          description: name that need to be deleted
+          required: true
+          schema:
+            type: string
+      responses:
+        '400':
+          description: Invalid user supplied
+        '404':
+          description: User not found
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/User'
+        description: Updated user object
+    delete:
+      tags:
+        - user
+      summary: Delete user
+      description: This can only be done by the logged in user.
+      operationId: deleteUser
+      parameters:
+        - name: username
+          in: path
+          description: The name that needs to be deleted
+          required: true
+          schema:
+            type: string
+      responses:
+        '400':
+          description: Invalid username supplied
+        '404':
+          description: User not found
+security:
+  - foo:
+      - bar
+      - baz
+    a:
+      - b
+      - c
+components:
+  parameters:
+    sharedSkip:
+      name: skip
+      in: query
+      description: Results to skip
+      required: false
+      schema:
+        type: integer
+        format: int32
+  responses:
+    veryBad:
+      description: failed
+  requestBodies:
+    UserArray:
+      content:
+        application/json:
+          schema:
+            type: array
+            items:
+              $ref: '#/components/schemas/User'
+      description: List of user object
+    Pet:
+      content:
+        application/json:
+          schema:
+            $ref: '#/components/schemas/Pet'
+        application/xml:
+          schema:
+            $ref: '#/components/schemas/Pet'
+      description: Pet object that needs to be added to the store
+  securitySchemes:
+    api_key:
+      type: apiKey
+      name: api_key
+      in: header
+    petstore_auth:
+      type: oauth2
+      flows:
+        implicit:
+          authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog'
+          scopes:
+            'write:pets': modify pets in your account
+            'read:pets': read your pets
+  schemas:
+    User:
+      properties:
+        id:
+          type: integer
+          format: int64
+        username:
+          type: string
+        firstName:
+          type: string
+        lastName:
+          type: string
+        email:
+          type: string
+        password:
+          type: string
+        phone:
+          type: string
+        userStatus:
+          type: integer
+          format: int32
+          description: User Status
+      xml:
+        name: User
+    Category:
+      properties:
+        id:
+          type: integer
+          format: int64
+        name:
+          type: string
+      xml:
+        name: Category
+    Pet:
+      required:
+        - name
+        - photoUrls
+      properties:
+        id:
+          type: integer
+          format: int64
+        category:
+          $ref: '#/components/schemas/Category'
+        name:
+          type: string
+          example: doggie
+        photoUrls:
+          type: array
+          xml:
+            name: photoUrl
+            wrapped: true
+          items:
+            type: string
+        tags:
+          type: array
+          xml:
+            name: tag
+            wrapped: true
+          items:
+            $ref: '#/components/schemas/Tag'
+        status:
+          type: string
+          description: pet status in the store
+      xml:
+        name: Pet
+    Tag:
+      properties:
+        id:
+          type: integer
+          format: int64
+        name:
+          type: string
+      xml:
+        name: Tag
+    Order:
+      properties:
+        id:
+          type: integer
+          format: int64
+        petId:
+          type: integer
+          format: int64
+        quantity:
+          type: integer
+          format: int32
+        shipDate:
+          type: string
+          format: date-time
+        status:
+          type: string
+          description: Order Status
+        complete:
+          type: boolean
+      xml:
+        name: Order
+    PetArray:
+      type: array
+      items:
+        $ref: '#/components/schemas/Pet'
diff --git a/swagger2markup-swagger-v3/src/test/resources/open_api/simple.yaml b/swagger2markup-swagger-v3/src/test/resources/open_api/simple.yaml
new file mode 100644
index 00000000..6ec4a0f8
--- /dev/null
+++ b/swagger2markup-swagger-v3/src/test/resources/open_api/simple.yaml
@@ -0,0 +1,115 @@
+openapi: 3.0.0
+info:
+  description: This is a simple API
+  version: "1.0.0"
+  title: Simple Inventory API
+  contact:
+    email: you@your-company.com
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+tags:
+  - name: admins
+    description: Secured Admin-only calls
+  - name: developers
+    description: Operations available to regular developers
+paths:
+  /inventory:
+    get:
+      tags:
+        - developers
+      summary: searches inventory
+      operationId: searchInventory
+      description: |
+        By passing in the appropriate options, you can search for
+        available inventory in the system
+      parameters:
+        - in: query
+          name: searchString
+          description: pass an optional search string for looking up inventory
+          required: false
+          schema:
+            type: string
+        - in: query
+          name: skip
+          description: number of records to skip for pagination
+          schema:
+            type: integer
+            format: int32
+            minimum: 0
+        - in: query
+          name: limit
+          description: maximum number of records to return
+          schema:
+            type: integer
+            format: int32
+            minimum: 0
+            maximum: 50
+      responses:
+        '200':
+          description: search results matching criteria
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/InventoryItem'
+        '400':
+          description: bad input parameter
+    post:
+      tags:
+        - admins
+      summary: adds an inventory item
+      operationId: addInventory
+      description: Adds an item to the system
+      responses:
+        '201':
+          description: item created
+        '400':
+          description: 'invalid input, object invalid'
+        '409':
+          description: an existing item already exists
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/InventoryItem'
+        description: Inventory item to add
+components:
+  schemas:
+    InventoryItem:
+      type: object
+      required:
+        - id
+        - name
+        - manufacturer
+        - releaseDate
+      properties:
+        id:
+          type: string
+          format: uuid
+          example: d290f1ee-6c54-4b01-90e6-d701748f0851
+        name:
+          type: string
+          example: Widget Adapter
+        releaseDate:
+          type: string
+          format: date-time
+          example: '2016-08-29T09:12:33.001Z'
+        manufacturer:
+          $ref: '#/components/schemas/Manufacturer'
+    Manufacturer:
+      required:
+        - name
+      properties:
+        name:
+          type: string
+          example: ACME Corporation
+        homePage:
+          type: string
+          format: url
+          example: 'https://www.acme-corp.com'
+        phone:
+          type: string
+          example: 408-867-5309
+      type: object