This is an automated email from the ASF dual-hosted git repository. pkarwasz pushed a commit to branch feature/asciidoctor-converter in repository https://gitbox.apache.org/repos/asf/logging-log4j-tools.git
commit d022f0469d7a1e5057688164089863ac3664e55e Author: Piotr P. Karwasz <[email protected]> AuthorDate: Wed Jan 31 23:18:27 2024 +0100 Add Javadoc to Asciidoc converter We add a converter between Javadoc and Asciidoc that converts: * from a [`DocCommentTree`](https://docs.oracle.com/en/java/javase/17/docs/api/jdk.compiler/com/sun/source/doctree/DocCommentTree.html), provided by the `javac` compiler or `javadoc` tool, * to an AsciiDoctorJ [`Document`](https://javadoc.io/static/org.asciidoctor/asciidoctorj/3.0.0-alpha.2/org/asciidoctor/ast/Document.html). We provide a primitive implementation of the AsciiDoctorJ API that converts the AST back into a text document. --- log4j-docgen/pom.xml | 28 ++ .../log4j/docgen/processor/AsciidocConverter.java | 382 +++++++++++++++++++++ .../log4j/docgen/processor/internal/BlockImpl.java | 73 ++++ .../log4j/docgen/processor/internal/CellImpl.java | 113 ++++++ .../docgen/processor/internal/ContentNodeImpl.java | 190 ++++++++++ .../docgen/processor/internal/DocumentImpl.java | 124 +++++++ .../log4j/docgen/processor/internal/ListImpl.java | 96 ++++++ .../docgen/processor/internal/ListItemImpl.java | 74 ++++ .../log4j/docgen/processor/internal/RowImpl.java | 32 ++ .../docgen/processor/internal/SectionImpl.java | 108 ++++++ .../processor/internal/StructuralNodeImpl.java | 158 +++++++++ .../log4j/docgen/processor/internal/TableImpl.java | 120 +++++++ .../src/test/it/example/JavadocExample.java | 100 ++++++ .../docgen/processor/AsciidocConverterTest.java | 133 +++++++ .../expected/processor/JavadocExample.adoc | 107 ++++++ log4j-tools-parent/pom.xml | 16 + 16 files changed, 1854 insertions(+) diff --git a/log4j-docgen/pom.xml b/log4j-docgen/pom.xml index d8d735f..11fd992 100644 --- a/log4j-docgen/pom.xml +++ b/log4j-docgen/pom.xml @@ -27,6 +27,7 @@ <artifactId>log4j-docgen</artifactId> <properties> + <maven.compiler.release>17</maven.compiler.release> <bnd.baseline.fail.on.missing>false</bnd.baseline.fail.on.missing> </properties> @@ -38,11 +39,38 @@ <scope>provided</scope> </dependency> + <dependency> + <groupId>org.asciidoctor</groupId> + <artifactId>asciidoctorj-api</artifactId> + <version>3.0.0-alpha.2</version> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-plugins</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/AsciidocConverter.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/AsciidocConverter.java new file mode 100644 index 0000000..41772c4 --- /dev/null +++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/AsciidocConverter.java @@ -0,0 +1,382 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.docgen.processor; + +import com.sun.source.doctree.DocCommentTree; +import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.DocTreeVisitor; +import com.sun.source.doctree.EndElementTree; +import com.sun.source.doctree.LiteralTree; +import com.sun.source.doctree.StartElementTree; +import com.sun.source.doctree.TextTree; +import com.sun.source.util.DocTrees; +import com.sun.source.util.SimpleDocTreeVisitor; +import java.util.ArrayList; +import java.util.EmptyStackException; +import java.util.function.Function; +import javax.lang.model.element.Element; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.docgen.processor.internal.BlockImpl; +import org.apache.logging.log4j.docgen.processor.internal.CellImpl; +import org.apache.logging.log4j.docgen.processor.internal.DocumentImpl; +import org.apache.logging.log4j.docgen.processor.internal.ListImpl; +import org.apache.logging.log4j.docgen.processor.internal.ListItemImpl; +import org.apache.logging.log4j.docgen.processor.internal.RowImpl; +import org.apache.logging.log4j.docgen.processor.internal.SectionImpl; +import org.apache.logging.log4j.docgen.processor.internal.TableImpl; +import org.asciidoctor.ast.Block; +import org.asciidoctor.ast.Cell; +import org.asciidoctor.ast.Document; +import org.asciidoctor.ast.List; +import org.asciidoctor.ast.ListItem; +import org.asciidoctor.ast.Row; +import org.asciidoctor.ast.Section; +import org.asciidoctor.ast.StructuralNode; +import org.asciidoctor.ast.Table; + +/** + * Converts a {@link DocCommentTree} into AsciiDoc text. + */ +class AsciidocConverter { + + private static final DocTreeVisitor<Void, Data> TO_ASCIIDOC_VISITOR = new ToAsciiDocVisitor(); + + private final DocTrees docTrees; + + AsciidocConverter(final DocTrees docTrees) { + this.docTrees = docTrees; + } + + public String toAsciiDoc(final Element element) { + final DocCommentTree tree = docTrees.getDocCommentTree(element); + return tree != null ? toAsciiDoc(tree, element.getSimpleName().toString()) : null; + } + + public String toAsciiDoc(final DocCommentTree tree, final String title) { + final Data data = new Data(title); + tree.accept(TO_ASCIIDOC_VISITOR, data); + return data.getDocument().convert(); + } + + private static class ToAsciiDocVisitor extends SimpleDocTreeVisitor<Void, Data> { + + @Override + public Void visitDocComment(final DocCommentTree node, final Data data) { + // Summary block wrapped in a new paragraph. + for (final DocTree docTree : node.getFirstSentence()) { + docTree.accept(this, data); + } + data.newParagraph(); + // Body + for (final DocTree docTree : node.getBody()) { + docTree.accept(this, data); + } + // Flushes the last paragraph + data.newParagraph(); + return super.visitDocComment(node, data); + } + + @Override + public Void visitStartElement(final StartElementTree node, final Data data) { + final String elementName = node.getName().toString(); + switch (elementName) { + case "p": + data.newParagraph(); + break; + case "ol": + // Nested list without a first paragraph + if (data.getCurrentNode() instanceof ListItem) { + data.newParagraph(); + } + data.pushChildNode(ListImpl::new).setContext(ListImpl.ORDERED_LIST_CONTEXT); + break; + case "ul": + // Nested list without a first paragraph + if (data.getCurrentNode() instanceof ListItem) { + data.newParagraph(); + } + data.pushChildNode(ListImpl::new).setContext(ListImpl.UNORDERED_LIST_CONTEXT); + break; + case "li": + if (!(data.getCurrentNode() instanceof List)) { + throw new IllegalArgumentException("A <li> tag must be a child of a <ol> or <ul> tag."); + } + data.pushChildNode(ListItemImpl::new); + break; + case "h1": + case "h2": + case "h3": + case "h4": + case "h5": + case "h6": + // Flush the current paragraph + data.newParagraph(); + StructuralNode currentNode; + // Remove other types of nodes from stack + while ((currentNode = data.getCurrentNode()) != null + && !(currentNode instanceof Section || currentNode instanceof Document)) { + data.popNode(); + } + break; + case "table": + data.pushChildNode(TableImpl::new); + break; + case "tr": + break; + case "th": + data.pushChildNode(CellImpl::new).setContext(CellImpl.HEADER_CONTEXT); + break; + case "td": + data.pushChildNode(CellImpl::new); + break; + case "pre": + data.newParagraph(); + final Block currentParagraph = data.getCurrentParagraph(); + currentParagraph.setContext(BlockImpl.LISTING_CONTEXT); + currentParagraph.setStyle(BlockImpl.SOURCE_STYLE); + break; + default: + } + return super.visitStartElement(node, data); + } + + @Override + public Void visitEndElement(final EndElementTree node, final Data data) { + final String elementName = node.getName().toString(); + switch (elementName) { + case "p": + // Ignore closing tags. + break; + case "ol": + case "ul": + case "li": + case "table": + case "th": + case "td": + data.popNode(); + break; + case "h1": + case "h2": + case "h3": + case "h4": + case "h5": + case "h6": + // Only flush the current line + if (!data.getCurrentLine().isEmpty()) { + data.newLine(); + } + // The current paragraph contains the title + // We retrieve the text and empty the paragraph + final Block currentParagraph = data.getCurrentParagraph(); + final String title = StringUtils.normalizeSpace(currentParagraph.convert()); + currentParagraph.setLines(new ArrayList<>()); + + // There should be no <h1> tags + final int newLevel = "h1".equals(elementName) ? 2 : elementName.charAt(1) - '0'; + data.setCurrentSectionLevel(newLevel); + data.getCurrentNode().setTitle(title); + break; + case "pre": + data.newParagraph(); + break; + case "tr": + // We group the new cells into a row + final Table table = (Table) data.getCurrentNode(); + final java.util.List<StructuralNode> cells = table.getBlocks(); + // First index of the row + int idx = 0; + for (final Row row : table.getHeader()) { + idx += row.getCells().size(); + } + for (final Row row : table.getBody()) { + idx += row.getCells().size(); + } + final Row row = new RowImpl(); + String context = CellImpl.BODY_CONTEXT; + for (int i = idx; i < table.getBlocks().size(); i++) { + final StructuralNode cell = cells.get(i); + context = cell.getContext(); + row.getCells().add((Cell) cell); + } + if (CellImpl.HEADER_CONTEXT.equals(context)) { + table.getHeader().add(row); + } else { + table.getBody().add(row); + } + break; + default: + } + return super.visitEndElement(node, data); + } + + @Override + public Void visitLiteral(final LiteralTree node, final Data data) { + if (node.getKind() == DocTree.Kind.CODE) { + if (!data.getCurrentLine().isEmpty()) { + data.append(" "); + } + data.append("`").append(node.getBody().getBody()).append("`"); + } else { + node.getBody().accept(this, data); + } + return super.visitLiteral(node, data); + } + + @Override + public Void visitText(final TextTree node, final Data data) { + final Block currentParagraph = data.getCurrentParagraph(); + if (BlockImpl.PARAGRAPH_CONTEXT.equals(currentParagraph.getContext())) { + appendSentences(node.getBody(), data); + } else { + data.append(node.getBody()); + } + return super.visitText(node, data); + } + + private static void appendSentences(final String text, final Data data) { + final String body = StringUtils.normalizeSpace(text); + final String[] sentences = body.split("(?<=[.!?])", -1); + // Full sentences + for (int i = 0; i < sentences.length - 1; i++) { + data.appendWords(sentences[i].strip()); + data.newLine(); + } + // Partial sentence + data.appendWords(sentences[sentences.length - 1]); + } + } + + private static class Data { + private final Document document; + private int currentSectionLevel; + private StructuralNode currentNode; + // not attached to the current node + private Block currentParagraph; + private final StringBuilder currentLine; + + public Data(final String title) { + document = new DocumentImpl(title); + currentSectionLevel = 1; + currentNode = document; + currentParagraph = new BlockImpl(currentNode); + currentLine = new StringBuilder(); + } + + public void newLine() { + // Remove trailing space + final String line = currentLine.toString().stripTrailing(); + // Ignore leading empty lines + if (!currentParagraph.getLines().isEmpty() || !line.isEmpty()) { + currentParagraph.getLines().add(line); + } + currentLine.setLength(0); + } + + public Data append(final String text) { + final String[] lines = text.split("\r?\n", -1); + for (int i = 0; i < lines.length; i++) { + currentLine.append(lines[i]); + if (i != lines.length - 1) { + newLine(); + } + } + return this; + } + + public void appendWords(final String words) { + // Separate text from previous words + if (!currentLine.isEmpty() && !words.isBlank()) { + currentLine.append(" "); + } + currentLine.append(words); + } + + public void newParagraph() { + newLine(); + final java.util.List<String> lines = currentParagraph.getLines(); + // Remove trailing empty lines + for (int i = lines.size() - 1; i >= 0; i--) { + if (lines.get(i).isEmpty()) { + lines.remove(i); + } + } + if (!currentParagraph.getLines().isEmpty()) { + currentNode.append(currentParagraph); + currentParagraph = new BlockImpl(currentNode); + } + } + + public StructuralNode getCurrentNode() { + return currentNode; + } + + public Block getCurrentParagraph() { + return currentParagraph; + } + + public StringBuilder getCurrentLine() { + return currentLine; + } + + public Document getDocument() { + return document; + } + + public void setCurrentSectionLevel(final int sectionLevel) { + while (sectionLevel < currentSectionLevel) { + popNode(); + currentSectionLevel--; + } + while (sectionLevel > currentSectionLevel) { + pushChildNode(SectionImpl::new); + currentSectionLevel++; + } + } + + /** + * Creates and appends a new child to the current node. + * @param supplier a function to create a new node that takes its parent node a parameter. + */ + public StructuralNode pushChildNode(final Function<? super StructuralNode, ? extends StructuralNode> supplier) { + // Flushes the current paragraph + newParagraph(); + + final StructuralNode child = supplier.apply(currentNode); + // Creates a new current paragraph + currentParagraph = new BlockImpl(child); + + currentNode.append(child); + return currentNode = child; + } + + public StructuralNode popNode() { + final StructuralNode currentNode = this.currentNode; + // Flushes the current paragraph + newParagraph(); + + final StructuralNode parent = (StructuralNode) currentNode.getParent(); + if (parent == null) { + throw new EmptyStackException(); + } + // Creates a new current paragraph + currentParagraph = new BlockImpl(parent); + + this.currentNode = parent; + return currentNode; + } + } +} diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/BlockImpl.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/BlockImpl.java new file mode 100644 index 0000000..a2dbfd8 --- /dev/null +++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/BlockImpl.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.docgen.processor.internal; + +import java.util.ArrayList; +import java.util.List; +import org.asciidoctor.ast.Block; +import org.asciidoctor.ast.ContentNode; + +public class BlockImpl extends StructuralNodeImpl implements Block { + + public static final String PARAGRAPH_CONTEXT = "paragraph"; + public static final String LISTING_CONTEXT = "listing"; + public static final String SOURCE_STYLE = "source"; + + private List<String> lines = new ArrayList<>(); + + public BlockImpl(final ContentNode parent) { + super(parent); + setContext(PARAGRAPH_CONTEXT); + } + + @Override + public void formatTo(final StringBuilder buffer) { + if (getStyle() != null) { + buffer.append('[').append(getStyle()).append("]\n"); + } + if (LISTING_CONTEXT.equals(getContext())) { + buffer.append("----\n"); + } + lines.forEach(line -> buffer.append(line).append('\n')); + if (LISTING_CONTEXT.equals(getContext())) { + buffer.append("----\n"); + } + } + + @Override + public List<String> getLines() { + return lines; + } + + @Override + public void setLines(final List<String> lines) { + this.lines = lines; + } + + // + // All methods below this line are not implemented. + // + @Override + public String getSource() { + throw new UnsupportedOperationException(); + } + + @Override + public void setSource(final String source) { + throw new UnsupportedOperationException(); + } +} diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/CellImpl.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/CellImpl.java new file mode 100644 index 0000000..a28c9a9 --- /dev/null +++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/CellImpl.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.docgen.processor.internal; + +import org.apache.logging.log4j.util.StringBuilderFormattable; +import org.asciidoctor.ast.Cell; +import org.asciidoctor.ast.Column; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.Document; +import org.asciidoctor.ast.Table; + +public class CellImpl extends StructuralNodeImpl implements Cell { + + public static final String HEADER_CONTEXT = "header"; + public static final String BODY_CONTEXT = "body"; + + public CellImpl(final ContentNode parent) { + super(parent); + setContext(BODY_CONTEXT); + } + + @Override + public void formatTo(final StringBuilder buffer) { + if (getBlocks().size() > 1) { + buffer.append('a'); + } + buffer.append("| "); + getBlocks().forEach(node -> { + if (node instanceof final StringBuilderFormattable formattable) { + formattable.formatTo(buffer); + } else { + buffer.append(node.convert()); + } + }); + } + + // + // All methods below this line are not implemented. + // + @Override + public Column getColumn() { + throw new UnsupportedOperationException(); + } + + @Override + public int getColspan() { + throw new UnsupportedOperationException(); + } + + @Override + public int getRowspan() { + throw new UnsupportedOperationException(); + } + + @Override + public String getText() { + throw new UnsupportedOperationException(); + } + + @Override + public String getSource() { + throw new UnsupportedOperationException(); + } + + @Override + public void setSource(final String source) { + throw new UnsupportedOperationException(); + } + + @Override + public Table.HorizontalAlignment getHorizontalAlignment() { + throw new UnsupportedOperationException(); + } + + @Override + public void setHorizontalAlignment(final Table.HorizontalAlignment halign) { + throw new UnsupportedOperationException(); + } + + @Override + public Table.VerticalAlignment getVerticalAlignment() { + throw new UnsupportedOperationException(); + } + + @Override + public void setVerticalAlignment(final Table.VerticalAlignment valign) { + throw new UnsupportedOperationException(); + } + + @Override + public Document getInnerDocument() { + throw new UnsupportedOperationException(); + } + + @Override + public void setInnerDocument(final Document document) { + throw new UnsupportedOperationException(); + } +} diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/ContentNodeImpl.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/ContentNodeImpl.java new file mode 100644 index 0000000..63e2b91 --- /dev/null +++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/ContentNodeImpl.java @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.docgen.processor.internal; + +import java.util.List; +import java.util.Map; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.Document; + +public abstract class ContentNodeImpl implements ContentNode { + + private final ContentNode parent; + private String context; + + protected ContentNodeImpl(final ContentNode parent) { + this.parent = parent; + } + + @Override + public ContentNode getParent() { + return parent; + } + + @Override + public String getContext() { + return context; + } + + @Override + public void setContext(final String context) { + this.context = context; + } + + @Override + public Document getDocument() { + return parent != null ? parent.getDocument() : null; + } + + // + // All methods below this line are not implemented. + // + @Override + public String getId() { + throw new UnsupportedOperationException(); + } + + @Override + public void setId(final String id) { + throw new UnsupportedOperationException(); + } + + @Override + public String getNodeName() { + throw new UnsupportedOperationException(); + } + + @Override + public Map<String, Object> getAttributes() { + throw new UnsupportedOperationException(); + } + + @Override + public Object getAttribute(final Object name, final Object defaultValue, final boolean inherit) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getAttribute(final Object name, final Object defaultValue) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getAttribute(final Object name) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasAttribute(final Object name) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasAttribute(final Object name, final boolean inherited) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isAttribute(final Object name, final Object expected) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isAttribute(final Object name, final Object expected, final boolean inherit) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean setAttribute(final Object name, final Object value, final boolean overwrite) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isOption(final Object name) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isRole() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasRole(final String role) { + throw new UnsupportedOperationException(); + } + + @Override + public String getRole() { + throw new UnsupportedOperationException(); + } + + @Override + public List<String> getRoles() { + throw new UnsupportedOperationException(); + } + + @Override + public void addRole(final String role) { + throw new UnsupportedOperationException(); + } + + @Override + public void removeRole(final String role) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isReftext() { + throw new UnsupportedOperationException(); + } + + @Override + public String getReftext() { + throw new UnsupportedOperationException(); + } + + @Override + public String iconUri(final String name) { + throw new UnsupportedOperationException(); + } + + @Override + public String mediaUri(final String target) { + throw new UnsupportedOperationException(); + } + + @Override + public String imageUri(final String targetImage) { + throw new UnsupportedOperationException(); + } + + @Override + public String imageUri(final String targetImage, final String assetDirKey) { + throw new UnsupportedOperationException(); + } + + @Override + public String readAsset(final String path, final Map<Object, Object> opts) { + throw new UnsupportedOperationException(); + } + + @Override + public String normalizeWebPath(final String path, final String start, final boolean preserveUriTarget) { + throw new UnsupportedOperationException(); + } +} diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/DocumentImpl.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/DocumentImpl.java new file mode 100644 index 0000000..c75a347 --- /dev/null +++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/DocumentImpl.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.docgen.processor.internal; + +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.util.StringBuilderFormattable; +import org.asciidoctor.ast.Author; +import org.asciidoctor.ast.Catalog; +import org.asciidoctor.ast.Document; +import org.asciidoctor.ast.RevisionInfo; +import org.asciidoctor.ast.StructuralNode; +import org.asciidoctor.ast.Title; + +public class DocumentImpl extends StructuralNodeImpl implements Document { + + public DocumentImpl(final String title) { + super(null); + setTitle(title); + } + + @Override + public void formatTo(final StringBuilder buffer) { + if (!StringUtils.isBlank(getTitle())) { + buffer.append("= ").append(getTitle()).append("\n\n"); + } + boolean first = true; + for (final StructuralNode node : getBlocks()) { + if (!first) { + buffer.append('\n'); + } else { + first = false; + } + if (node instanceof final StringBuilderFormattable formattable) { + formattable.formatTo(buffer); + } else { + buffer.append(node.convert()); + } + } + } + + @Override + public String getDoctitle() { + return getTitle(); + } + + // + // All methods below this line are not implemented. + // + @Override + public Title getStructuredDoctitle() { + throw new UnsupportedOperationException(); + } + + @Override + public List<Author> getAuthors() { + throw new UnsupportedOperationException(); + } + + @Override + public String getSource() { + throw new UnsupportedOperationException(); + } + + @Override + public List<String> getSourceLines() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isBasebackend(final String backend) { + throw new UnsupportedOperationException(); + } + + @Override + public Map<Object, Object> getOptions() { + throw new UnsupportedOperationException(); + } + + @Override + public int getAndIncrementCounter(final String name) { + throw new UnsupportedOperationException(); + } + + @Override + public int getAndIncrementCounter(final String name, final int initialValue) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSourcemap() { + throw new UnsupportedOperationException(); + } + + @Override + public void setSourcemap(final boolean state) { + throw new UnsupportedOperationException(); + } + + @Override + public Catalog getCatalog() { + throw new UnsupportedOperationException(); + } + + @Override + public RevisionInfo getRevisionInfo() { + throw new UnsupportedOperationException(); + } +} diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/ListImpl.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/ListImpl.java new file mode 100644 index 0000000..d802825 --- /dev/null +++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/ListImpl.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.docgen.processor.internal; + +import java.util.Objects; +import org.apache.logging.log4j.util.StringBuilderFormattable; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.List; +import org.asciidoctor.ast.ListItem; +import org.asciidoctor.ast.StructuralNode; + +public class ListImpl extends StructuralNodeImpl implements List { + + public static final String ORDERED_LIST_CONTEXT = "olist"; + private static final char ORDERED_LIST_MARKER = '.'; + public static final String UNORDERED_LIST_CONTEXT = "ulist"; + private static final char UNORDERED_LIST_MARKER = '*'; + + public ListImpl(final ContentNode parent) { + super(parent); + setContext(ORDERED_LIST_CONTEXT); + } + + @Override + public void formatTo(final StringBuilder buffer) { + final String prefix = computeItemPrefix(); + getBlocks().forEach(node -> { + buffer.append(prefix); + if (node instanceof final StringBuilderFormattable formattable) { + formattable.formatTo(buffer); + } else { + buffer.append(node.convert()); + } + }); + } + + /** + * Computes the appropriate prefix for the list. + */ + private String computeItemPrefix() { + final StringBuilder sb = new StringBuilder(); + ContentNode currentNode = this; + while (currentNode instanceof List) { + // If the type of list changes stop. + if (!Objects.equals(getContext(), currentNode.getContext())) { + break; + } + sb.append( + ORDERED_LIST_CONTEXT.equals(currentNode.getContext()) + ? ORDERED_LIST_MARKER + : UNORDERED_LIST_MARKER); + currentNode = currentNode.getParent(); + if (!(currentNode instanceof ListItem)) { + break; + } + currentNode = currentNode.getParent(); + } + return sb.reverse().append(' ').toString(); + } + + @Override + public java.util.List<StructuralNode> getItems() { + return getBlocks(); + } + + @Override + public boolean hasItems() { + return !getBlocks().isEmpty(); + } + + @Override + public void setContext(final String context) { + switch (context) { + case ORDERED_LIST_CONTEXT: + case UNORDERED_LIST_CONTEXT: + break; + default: + throw new RuntimeException("Unknown list context " + context); + } + super.setContext(context); + } +} diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/ListItemImpl.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/ListItemImpl.java new file mode 100644 index 0000000..2988041 --- /dev/null +++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/ListItemImpl.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.docgen.processor.internal; + +import org.apache.logging.log4j.util.StringBuilderFormattable; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.ListItem; +import org.asciidoctor.ast.StructuralNode; + +public class ListItemImpl extends StructuralNodeImpl implements ListItem { + + public ListItemImpl(final ContentNode parent) { + super(parent); + } + + @Override + public void formatTo(final StringBuilder buffer) { + boolean first = true; + for (final StructuralNode node : getBlocks()) { + if (!first) { + buffer.append("+\n"); + } else { + first = false; + } + if (node instanceof final StringBuilderFormattable formattable) { + formattable.formatTo(buffer); + } else { + buffer.append(node.convert()); + } + } + } + + // + // All methods below this line are not implemented. + // + @Override + public String getMarker() { + throw new UnsupportedOperationException(); + } + + @Override + public String getText() { + throw new UnsupportedOperationException(); + } + + @Override + public String getSource() { + throw new UnsupportedOperationException(); + } + + @Override + public void setSource(final String source) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasText() { + throw new UnsupportedOperationException(); + } +} diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/RowImpl.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/RowImpl.java new file mode 100644 index 0000000..6dff898 --- /dev/null +++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/RowImpl.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.docgen.processor.internal; + +import java.util.ArrayList; +import java.util.List; +import org.asciidoctor.ast.Cell; +import org.asciidoctor.ast.Row; + +public class RowImpl implements Row { + + private final List<Cell> cells = new ArrayList<>(); + + @Override + public List<Cell> getCells() { + return cells; + } +} diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/SectionImpl.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/SectionImpl.java new file mode 100644 index 0000000..6824f15 --- /dev/null +++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/SectionImpl.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.docgen.processor.internal; + +import java.util.ArrayList; +import java.util.List; +import org.apache.logging.log4j.util.StringBuilderFormattable; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.Document; +import org.asciidoctor.ast.Section; +import org.asciidoctor.ast.StructuralNode; + +public class SectionImpl extends StructuralNodeImpl implements Section { + + private final List<StructuralNode> content = new ArrayList<>(); + + public SectionImpl(final ContentNode parent) { + super(parent); + } + + @Override + public void formatTo(final StringBuilder buffer) { + final String title = getTitle(); + if (title != null) { + buffer.append("=".repeat(computeSectionLevel(this))) + .append(' ') + .append(title) + .append("\n\n"); + } + boolean first = true; + for (final StructuralNode node : getBlocks()) { + if (!first) { + buffer.append('\n'); + } else { + first = false; + } + if (node instanceof final StringBuilderFormattable formattable) { + formattable.formatTo(buffer); + } else { + buffer.append(node.convert()); + } + } + } + + private static int computeSectionLevel(final StructuralNode section) { + int level = 0; + StructuralNode currentNode = section; + while (currentNode != null) { + if (currentNode instanceof Section || currentNode instanceof Document) { + level++; + } + currentNode = (StructuralNode) currentNode.getParent(); + } + return level; + } + + @Override + public String getSectionName() { + return getTitle(); + } + + // + // All methods below this line are not implemented. + // + @Override + public int getIndex() { + throw new UnsupportedOperationException(); + } + + @Override + public String getNumeral() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSpecial() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isNumbered() { + throw new UnsupportedOperationException(); + } + + @Override + public String getSectnum() { + throw new UnsupportedOperationException(); + } + + @Override + public String getSectnum(final String delimiter) { + throw new UnsupportedOperationException(); + } +} diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/StructuralNodeImpl.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/StructuralNodeImpl.java new file mode 100644 index 0000000..402ae54 --- /dev/null +++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/StructuralNodeImpl.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.docgen.processor.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.apache.logging.log4j.util.StringBuilderFormattable; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.Cursor; +import org.asciidoctor.ast.StructuralNode; + +public abstract class StructuralNodeImpl extends ContentNodeImpl implements StructuralNode, StringBuilderFormattable { + + private int level; + private String title; + private String style; + + private final List<StructuralNode> blocks = new ArrayList<>(); + + public StructuralNodeImpl(final ContentNode parent) { + super(parent); + } + + @Override + public String getTitle() { + return title; + } + + @Override + public void setTitle(final String title) { + this.title = title; + } + + @Override + public String getStyle() { + return style; + } + + @Override + public void setStyle(final String style) { + this.style = style; + } + + @Override + public List<StructuralNode> getBlocks() { + return blocks; + } + + @Override + public void append(final StructuralNode structuralNode) { + blocks.add(structuralNode); + } + + @Override + public String convert() { + final StringBuilder sb = new StringBuilder(); + formatTo(sb); + return sb.toString(); + } + + @Override + public int getLevel() { + return level; + } + + @Override + public void setLevel(final int level) { + this.level = level; + } + + // + // All methods below this line are not implemented. + // + @Override + public List<StructuralNode> findBy(final Map<Object, Object> map) { + throw new UnsupportedOperationException(); + } + + @Override + public String getCaption() { + throw new UnsupportedOperationException(); + } + + @Override + public void setCaption(final String caption) { + throw new UnsupportedOperationException(); + } + + @Override + public String getContentModel() { + throw new UnsupportedOperationException(); + } + + @Override + public Cursor getSourceLocation() { + throw new UnsupportedOperationException(); + } + + @Override + public List<String> getSubstitutions() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSubstitutionEnabled(final String substitutionEnabled) { + throw new UnsupportedOperationException(); + } + + @Override + public void removeSubstitution(final String substitution) { + throw new UnsupportedOperationException(); + } + + @Override + public void addSubstitution(final String substitution) { + throw new UnsupportedOperationException(); + } + + @Override + public void prependSubstitution(final String substitution) { + throw new UnsupportedOperationException(); + } + + @Override + public void setSubstitutions(final String... substitutions) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getContent() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isInline() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isBlock() { + throw new UnsupportedOperationException(); + } +} diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/TableImpl.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/TableImpl.java new file mode 100644 index 0000000..c57b179 --- /dev/null +++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/processor/internal/TableImpl.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.docgen.processor.internal; + +import java.util.ArrayList; +import java.util.List; +import org.apache.logging.log4j.util.StringBuilderFormattable; +import org.asciidoctor.ast.Column; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.Row; +import org.asciidoctor.ast.Table; + +public class TableImpl extends StructuralNodeImpl implements Table { + + private final List<Row> header = new ArrayList<>(); + private final List<Row> body = new ArrayList<>(); + + public TableImpl(final ContentNode parent) { + super(parent); + } + + @Override + public void formatTo(final StringBuilder buffer) { + final int colCount = header.isEmpty() + ? body.isEmpty() ? 1 : body.get(0).getCells().size() + : header.get(0).getCells().size(); + + buffer.append("[cols=\""); + formatColSpecifier(colCount, buffer); + if (!header.isEmpty()) { + buffer.append("\",options=\"headers"); + } + buffer.append("\"]\n").append("|===\n"); + getHeader().forEach(row -> formatRow(row, buffer)); + getBody().forEach(row -> formatRow(row, buffer)); + buffer.append("\n|===\n"); + } + + private static void formatColSpecifier(final int colCount, final StringBuilder buffer) { + if (colCount > 0) { + buffer.append("1"); + } + for (int i = 1; i < colCount; i++) { + buffer.append(",1"); + } + } + + private void formatRow(final Row row, final StringBuilder buffer) { + buffer.append('\n'); + row.getCells().forEach(cell -> { + if (cell instanceof final StringBuilderFormattable formattable) { + formattable.formatTo(buffer); + } else { + buffer.append(cell.convert()); + } + }); + } + + @Override + public List<Row> getHeader() { + return header; + } + + @Override + public List<Row> getBody() { + return body; + } + + // + // All methods below this line are not implemented. + // + @Override + public List<Row> getFooter() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasHeaderOption() { + throw new UnsupportedOperationException(); + } + + @Override + public List<Column> getColumns() { + throw new UnsupportedOperationException(); + } + + @Override + public String getFrame() { + throw new UnsupportedOperationException(); + } + + @Override + public void setFrame(final String frame) { + throw new UnsupportedOperationException(); + } + + @Override + public String getGrid() { + throw new UnsupportedOperationException(); + } + + @Override + public void setGrid(final String grid) { + throw new UnsupportedOperationException(); + } +} diff --git a/log4j-docgen/src/test/it/example/JavadocExample.java b/log4j-docgen/src/test/it/example/JavadocExample.java new file mode 100644 index 0000000..5ddf2c4 --- /dev/null +++ b/log4j-docgen/src/test/it/example/JavadocExample.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example; + +/** + * Example of JavaDoc to AsciiDoc conversion + * <p> + * We run the {@code javadoc} tool on this class to test conversion of JavaDoc comments to AsciiDoc. This + * paragraph has two sentences. + * </p> + * <p> + * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum blandit dictum sem, ornare posuere lorem + * convallis sit amet. Sed dui augue, faucibus ut nisi id, mollis euismod nibh. Donec lobortis luctus viverra. In + * orci ante, pretium et fringilla at, sagittis nec justo. Cras finibus lorem vel volutpat interdum. Sed laoreet + * libero eros, ac cursus nibh dapibus vitae. Integer ante lorem, rhoncus at tortor vel, congue accumsan lorem. + * In hac habitasse platea dictumst. Nunc luctus ornare justo. Etiam ut metus id tortor dignissim semper. Nam + * turpis dui, aliquet nec enim et, faucibus accumsan dui. + *</p> + * <p> + * Aenean tincidunt elit id posuere mattis. Fusce bibendum sapien sed risus ultricies, non molestie erat volutpat. + * Donec nisi felis, egestas eu lobortis id, vulputate nec felis. In at dui euismod, blandit nulla et, accumsan + * elit. Proin id venenatis dui. Suspendisse sit amet est ut neque tincidunt venenatis. Donec bibendum quis velit + * fermentum porttitor. Maecenas faucibus, eros sit amet maximus malesuada, turpis neque bibendum justo, eu + * vehicula justo metus a ipsum. In at ullamcorper ipsum. Quisque in vehicula erat. Proin vitae suscipit dui, + * rutrum hendrerit augue. Curabitur finibus feugiat elit. + * </p> + * <ol> + * <li>Item with a nested ordered list. + * <ol> + * <li>First nested item.</li> + * <li>Second nested item.</li> + * </ol> + * </li> + * <li>Item with a nested unordered list. + * <ul> + * <li>Unordered list item.</li> + * </ul> + * </li> + * <li> + * <p> + * Item with complex content + * </p> + * <p> + * Mauris suscipit velit nec ligula mattis, nec varius augue accumsan. Curabitur a dolor dui. Quisque + * congue facilisis est nec dignissim. Pellentesque egestas eleifend faucibus. Fusce imperdiet ligula a + * lectus fringilla varius. Sed malesuada porta vulputate. Sed vulputate purus nec nibh interdum + * convallis. Cras faucibus, dolor tempus lacinia vehicula, elit risus luctus libero, sed molestie nisi + * lorem sit amet enim. Integer vitae enim sagittis, malesuada lorem at, interdum tellus. Suspendisse + * potenti. Vestibulum ac nisi sit amet ex dictum suscipit. Nulla varius augue a velit tincidunt feugiat. + * Proin fringilla id leo ut dignissim. Vivamus eu tellus eget orci suscipit viverra. Donec sodales et + * arcu vel mollis. + * </p> + * <p> + * Praesent gravida auctor lectus quis interdum. Etiam semper mauris quis neque bibendum molestie. + * Maecenas a lacus nec risus pellentesque accumsan. Suspendisse dictum dui eleifend nibh facilisis, non + * consequat neque elementum. Donec scelerisque ultricies ipsum, pretium elementum ex pellentesque + * malesuada. Mauris egestas massa vitae sapien lobortis convallis. Donec feugiat, purus commodo + * consequat vehicula, dolor urna aliquam arcu, id rutrum quam tortor quis libero. Sed varius justo eget + * congue lacinia. + * </p> + * </li> + * </ol> + * <ul> + * <li>Item of an unordered list.</li> + * </ul> + * <h2>First section</h2> + * <table> + * <tr> + * <th>Key</th> + * <th>Value</th> + * </tr> + * <tr> + * <td>A</td> + * <td>1</td> + * </tr> + * <tr> + * <td>B</td> + * <td>2</td> + * </tr> + * </table> + * <h3>Subsection</h3> + * <pre> + * private static final Logger logger = LogManager.getLogger(); + * </pre> + */ +public class JavadocExample {} diff --git a/log4j-docgen/src/test/java/org/apache/logging/log4j/docgen/processor/AsciidocConverterTest.java b/log4j-docgen/src/test/java/org/apache/logging/log4j/docgen/processor/AsciidocConverterTest.java new file mode 100644 index 0000000..5eb71b7 --- /dev/null +++ b/log4j-docgen/src/test/java/org/apache/logging/log4j/docgen/processor/AsciidocConverterTest.java @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.docgen.processor; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.DocumentationTool; +import javax.tools.DocumentationTool.DocumentationTask; +import javax.tools.FileObject; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; +import javax.tools.ToolProvider; +import jdk.javadoc.doclet.Doclet; +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.doclet.Reporter; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AsciidocConverterTest { + + private static final Path LICENSE_PATH; + + static { + try { + LICENSE_PATH = Paths.get(AsciidocConverterTest.class + .getResource("/templates/license.ftl") + .toURI()); + } catch (final URISyntaxException e) { + throw new RuntimeException(e); + } + } + + @Test + void convertToAsciidoc() throws Exception { + final DocumentationTool tool = ToolProvider.getSystemDocumentationTool(); + final DiagnosticCollector<JavaFileObject> ds = new DiagnosticCollector<>(); + final StandardJavaFileManager fileManager = tool.getStandardFileManager(null, Locale.ROOT, UTF_8); + + final Path basePath = Paths.get(System.getProperty("basedir", ".")); + final Path sourcePath = basePath.resolve("src/test/it/example/JavadocExample.java"); + final Iterable<? extends JavaFileObject> sources = fileManager.getJavaFileObjectsFromPaths(List.of(sourcePath)); + + final Path destPath = basePath.resolve("target/test-site"); + Files.createDirectories(destPath); + fileManager.setLocationFromPaths(StandardLocation.CLASS_OUTPUT, Set.of(destPath)); + + final DocumentationTask task = tool.getTask(null, fileManager, ds, TestDoclet.class, null, sources); + task.call(); + + final List<String> warnings = ds.getDiagnostics().stream() + .filter(d -> d.getKind() != Diagnostic.Kind.NOTE) + .map(d -> d.getMessage(Locale.ROOT)) + .toList(); + Assertions.assertThat(warnings).isEmpty(); + final Path expectedPath = Paths.get(AsciidocConverterTest.class + .getResource("/expected/processor/JavadocExample.adoc") + .toURI()); + Assertions.assertThat(destPath.resolve("processor/JavadocExample.adoc")) + .hasSameTextualContentAs(expectedPath, UTF_8); + } + + public static class TestDoclet implements Doclet { + + @Override + public void init(final Locale locale, final Reporter reporter) {} + + @Override + public String getName() { + return "test"; + } + + @Override + public Set<? extends Option> getSupportedOptions() { + return Set.of(); + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.RELEASE_17; + } + + @Override + public boolean run(final DocletEnvironment environment) { + final AsciidocConverter converter = new AsciidocConverter(environment.getDocTrees()); + final JavaFileManager fileManager = environment.getJavaFileManager(); + try { + for (final Element element : environment.getIncludedElements()) { + if ("JavadocExample".equals(element.getSimpleName().toString())) { + final FileObject output = fileManager.getFileForOutput( + StandardLocation.CLASS_OUTPUT, "processor", "JavadocExample.adoc", null); + final String asciiDoc = converter.toAsciiDoc(element); + try (final OutputStream os = output.openOutputStream()) { + Files.copy(LICENSE_PATH, os); + os.write(asciiDoc.getBytes(UTF_8)); + } + } + } + } catch (final IOException e) { + throw new RuntimeException(e); + } + return true; + } + } +} diff --git a/log4j-docgen/src/test/resources/expected/processor/JavadocExample.adoc b/log4j-docgen/src/test/resources/expected/processor/JavadocExample.adoc new file mode 100644 index 0000000..b14e4e6 --- /dev/null +++ b/log4j-docgen/src/test/resources/expected/processor/JavadocExample.adoc @@ -0,0 +1,107 @@ +//// +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +//// += JavadocExample + +Example of JavaDoc to AsciiDoc conversion + +We run the `javadoc` tool on this class to test conversion of JavaDoc comments to AsciiDoc. +This paragraph has two sentences. + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. +Vestibulum blandit dictum sem, ornare posuere lorem convallis sit amet. +Sed dui augue, faucibus ut nisi id, mollis euismod nibh. +Donec lobortis luctus viverra. +In orci ante, pretium et fringilla at, sagittis nec justo. +Cras finibus lorem vel volutpat interdum. +Sed laoreet libero eros, ac cursus nibh dapibus vitae. +Integer ante lorem, rhoncus at tortor vel, congue accumsan lorem. +In hac habitasse platea dictumst. +Nunc luctus ornare justo. +Etiam ut metus id tortor dignissim semper. +Nam turpis dui, aliquet nec enim et, faucibus accumsan dui. + +Aenean tincidunt elit id posuere mattis. +Fusce bibendum sapien sed risus ultricies, non molestie erat volutpat. +Donec nisi felis, egestas eu lobortis id, vulputate nec felis. +In at dui euismod, blandit nulla et, accumsan elit. +Proin id venenatis dui. +Suspendisse sit amet est ut neque tincidunt venenatis. +Donec bibendum quis velit fermentum porttitor. +Maecenas faucibus, eros sit amet maximus malesuada, turpis neque bibendum justo, eu vehicula justo metus a ipsum. +In at ullamcorper ipsum. +Quisque in vehicula erat. +Proin vitae suscipit dui, rutrum hendrerit augue. +Curabitur finibus feugiat elit. + +. Item with a nested ordered list. ++ +.. First nested item. +.. Second nested item. +. Item with a nested unordered list. ++ +* Unordered list item. +. Item with complex content ++ +Mauris suscipit velit nec ligula mattis, nec varius augue accumsan. +Curabitur a dolor dui. +Quisque congue facilisis est nec dignissim. +Pellentesque egestas eleifend faucibus. +Fusce imperdiet ligula a lectus fringilla varius. +Sed malesuada porta vulputate. +Sed vulputate purus nec nibh interdum convallis. +Cras faucibus, dolor tempus lacinia vehicula, elit risus luctus libero, sed molestie nisi lorem sit amet enim. +Integer vitae enim sagittis, malesuada lorem at, interdum tellus. +Suspendisse potenti. +Vestibulum ac nisi sit amet ex dictum suscipit. +Nulla varius augue a velit tincidunt feugiat. +Proin fringilla id leo ut dignissim. +Vivamus eu tellus eget orci suscipit viverra. +Donec sodales et arcu vel mollis. ++ +Praesent gravida auctor lectus quis interdum. +Etiam semper mauris quis neque bibendum molestie. +Maecenas a lacus nec risus pellentesque accumsan. +Suspendisse dictum dui eleifend nibh facilisis, non consequat neque elementum. +Donec scelerisque ultricies ipsum, pretium elementum ex pellentesque malesuada. +Mauris egestas massa vitae sapien lobortis convallis. +Donec feugiat, purus commodo consequat vehicula, dolor urna aliquam arcu, id rutrum quam tortor quis libero. +Sed varius justo eget congue lacinia. + +* Item of an unordered list. + +== First section + +[cols="1,1",options="headers"] +|=== + +| Key +| Value + +| A +| 1 + +| B +| 2 + +|=== + +=== Subsection + +[source] +---- + private static final Logger logger = LogManager.getLogger(); +---- diff --git a/log4j-tools-parent/pom.xml b/log4j-tools-parent/pom.xml index 73d8708..02871c4 100644 --- a/log4j-tools-parent/pom.xml +++ b/log4j-tools-parent/pom.xml @@ -34,9 +34,11 @@ <!-- Dependency versions --> <assertj.version>3.25.1</assertj.version> <commons-io.version>2.15.1</commons-io.version> + <commons-lang3.version>3.14.0</commons-lang3.version> <freemarker.version>2.3.32</freemarker.version> <jakarta.inject.version>1.0.5</jakarta.inject.version> <junit.version>5.10.1</junit.version> + <log4j-bom.version>3.0.0-beta1</log4j-bom.version> <modello.version>2.1.2</modello.version> <xmlunit.version>2.9.1</xmlunit.version> @@ -51,6 +53,14 @@ <dependencyManagement> <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-bom</artifactId> + <version>${log4j-bom.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + <dependency> <groupId>org.junit</groupId> <artifactId>junit-bom</artifactId> @@ -71,6 +81,12 @@ <version>${commons-io.version}</version> </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>${commons-lang3.version}</version> + </dependency> + <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId>
