diff --git a/modello-plugins/modello-plugin-xdoc/pom.xml b/modello-plugins/modello-plugin-xdoc/pom.xml index 31cb085c0..8bc64e131 100644 --- a/modello-plugins/modello-plugin-xdoc/pom.xml +++ b/modello-plugins/modello-plugin-xdoc/pom.xml @@ -25,6 +25,16 @@ org.codehaus.plexus plexus-utils + + org.apache.maven.doxia + doxia-sink-api + 2.0.0 + + + org.apache.maven.doxia + doxia-module-xdoc + 2.0.0 + org.jsoup jsoup diff --git a/modello-plugins/modello-plugin-xdoc/src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java b/modello-plugins/modello-plugin-xdoc/src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java index c9746bf43..4a1e358c4 100644 --- a/modello-plugins/modello-plugin-xdoc/src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java +++ b/modello-plugins/modello-plugin-xdoc/src/main/java/org/codehaus/modello/plugin/xdoc/XdocGenerator.java @@ -25,8 +25,9 @@ import javax.inject.Named; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; -import java.io.Writer; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; @@ -36,6 +37,9 @@ import com.github.chhorz.javadoc.OutputType; import com.github.chhorz.javadoc.tags.BlockTag; import com.github.chhorz.javadoc.tags.SinceTag; +import org.apache.maven.doxia.module.xdoc.XdocSinkFactory; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.doxia.sink.SinkFactory; import org.codehaus.modello.ModelloException; import org.codehaus.modello.ModelloParameterConstants; import org.codehaus.modello.ModelloRuntimeException; @@ -56,9 +60,6 @@ import org.codehaus.modello.plugins.xml.metadata.XmlFieldMetadata; import org.codehaus.modello.plugins.xml.metadata.XmlModelMetadata; import org.codehaus.plexus.util.StringUtils; -import org.codehaus.plexus.util.io.CachingWriter; -import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter; -import org.codehaus.plexus.util.xml.XMLWriter; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -115,50 +116,49 @@ private void generateXdoc(Map parameters) throws IOException { f = new File(directory, xdocFileName); } - Writer writer = new CachingWriter(f, StandardCharsets.UTF_8); + OutputStream outputStream = new FileOutputStream(f); + SinkFactory sinkFactory = new XdocSinkFactory(); + Sink sink = sinkFactory.createSink(outputStream, StandardCharsets.UTF_8.name()); - XMLWriter w = new PrettyPrintXMLWriter(writer); - - writer.write("\n"); - - initHeader(w); - - w.startElement("document"); - - w.startElement("properties"); - - writeTextElement(w, "title", objectModel.getName()); - - w.endElement(); + // Start document + sink.head(); + sink.title(); + sink.text(objectModel.getName()); + sink.title_(); + sink.head_(); // Body + sink.body(); - w.startElement("body"); - - w.startElement("section"); + sink.section1(); + sink.sectionTitle1(); + sink.text(objectModel.getName()); + sink.sectionTitle1_(); - w.addAttribute("name", objectModel.getName()); - - writeMarkupElement(w, "p", getDescription(objectModel)); + sink.paragraph(); + writeMarkupViaSink(sink, getDescription(objectModel)); + sink.paragraph_(); // XML representation of the model with links ModelClass root = objectModel.getClass(objectModel.getRoot(getGeneratedVersion()), getGeneratedVersion()); - writeMarkupElement(w, "source", "\n" + getModelXmlDescriptor(root)); + sink.verbatim(null); + sink.rawText("\n" + getModelXmlDescriptor(root)); + sink.verbatim_(); // Element descriptors // Traverse from root so "abstract" models aren't included - writeModelDescriptor(w, root); - - w.endElement(); + writeModelDescriptor(sink, root); - w.endElement(); + sink.section1_(); - w.endElement(); + sink.body_(); - writer.flush(); + sink.flush(); + sink.close(); - writer.close(); + outputStream.flush(); + outputStream.close(); } /** @@ -179,24 +179,24 @@ private String getAnchorName(String tagName, ModelClass modelClass) { /** * Write description of the whole model. * - * @param w the output writer + * @param sink the Doxia sink * @param rootModelClass the root class of the model */ - private void writeModelDescriptor(XMLWriter w, ModelClass rootModelClass) { - writeElementDescriptor(w, rootModelClass, null, new HashSet<>(), new HashMap<>()); + private void writeModelDescriptor(Sink sink, ModelClass rootModelClass) { + writeElementDescriptor(sink, rootModelClass, null, new HashSet<>(), new HashMap<>()); } /** * Write description of an element of the XML representation of the model. This method is recursive. * - * @param w the output writer + * @param sink the Doxia sink * @param modelClass the mode class to describe * @param association the association we are coming from (can be null) * @param writtenIds set of data already written ids * @param writtenAnchors map of already written anchors with corresponding ids */ private void writeElementDescriptor( - XMLWriter w, + Sink sink, ModelClass modelClass, ModelAssociation association, Set writtenIds, @@ -219,17 +219,17 @@ private void writeElementDescriptor( writtenAnchors.put(anchorName, id); } - w.startElement("a"); + sink.anchor(anchorName); + sink.anchor_(); - w.addAttribute("name", anchorName); + sink.section2(); + sink.sectionTitle2(); + sink.text(tagName); + sink.sectionTitle2_(); - w.endElement(); - - w.startElement("subsection"); - - w.addAttribute("name", tagName); - - writeMarkupElement(w, "p", getDescription(modelClass)); + sink.paragraph(); + writeMarkupViaSink(sink, getDescription(modelClass)); + sink.paragraph_(); List elementFields = getFieldsForXml(modelClass, getGeneratedVersion()); @@ -237,23 +237,22 @@ private void writeElementDescriptor( if (contentField != null) { // this model class has a Content field - w.startElement("p"); - - writeTextElement(w, "b", "Element Content: "); - - w.writeMarkup(getDescription(contentField)); - - w.endElement(); + sink.paragraph(); + sink.bold(); + sink.text("Element Content: "); + sink.bold_(); + writeMarkupViaSink(sink, getDescription(contentField)); + sink.paragraph_(); } List attributeFields = getXmlAttributeFields(elementFields); elementFields.removeAll(attributeFields); - writeFieldsTable(w, attributeFields, false); // write attributes - writeFieldsTable(w, elementFields, true); // write elements + writeFieldsTable(sink, attributeFields, false); // write attributes + writeFieldsTable(sink, elementFields, true); // write elements - w.endElement(); + sink.section2_(); // check every fields that are inner associations to write their element descriptor for (ModelField f : elementFields) { @@ -262,7 +261,7 @@ private void writeElementDescriptor( ModelClass fieldModelClass = getModel().getClass(assoc.getTo(), getGeneratedVersion()); if (!writtenIds.contains(getId(resolveTagName(fieldModelClass, assoc), fieldModelClass))) { - writeElementDescriptor(w, fieldModelClass, assoc, writtenIds, writtenAnchors); + writeElementDescriptor(sink, fieldModelClass, assoc, writtenIds, writtenAnchors); } } } @@ -275,11 +274,11 @@ private String getId(String tagName, ModelClass modelClass) { /** * Write a table containing model fields description. * - * @param w the output writer + * @param sink the Doxia sink * @param fields the fields to add in the table * @param elementFields true if fields are elements, false if fields are attributes */ - private void writeFieldsTable(XMLWriter w, List fields, boolean elementFields) { + private void writeFieldsTable(Sink sink, List fields, boolean elementFields) { if (fields == null || fields.isEmpty()) { // skip empty table return; @@ -290,23 +289,32 @@ private void writeFieldsTable(XMLWriter w, List fields, boolean elem return; } - w.startElement("table"); + // Workaround: Doxia Sink's table() method doesn't seem to output tag + sink.rawText("
"); - w.startElement("tr"); + sink.tableRow(); - writeTextElement(w, "th", elementFields ? "Element" : "Attribute"); + sink.tableHeaderCell(); + sink.text(elementFields ? "Element" : "Attribute"); + sink.tableHeaderCell_(); - writeTextElement(w, "th", "Type"); + sink.tableHeaderCell(); + sink.text("Type"); + sink.tableHeaderCell_(); boolean showSinceColumn = version.greaterThan(firstVersion); if (showSinceColumn) { - writeTextElement(w, "th", "Since"); + sink.tableHeaderCell(); + sink.text("Since"); + sink.tableHeaderCell_(); } - writeTextElement(w, "th", "Description"); + sink.tableHeaderCell(); + sink.text("Description"); + sink.tableHeaderCell_(); - w.endElement(); // tr + sink.tableRow_(); for (ModelField f : fields) { XmlFieldMetadata xmlFieldMetadata = (XmlFieldMetadata) f.getMetadata(XmlFieldMetadata.ID); @@ -315,15 +323,15 @@ private void writeFieldsTable(XMLWriter w, List fields, boolean elem continue; } - w.startElement("tr"); + sink.tableRow(); // Element/Attribute column String tagName = resolveTagName(f, xmlFieldMetadata); - w.startElement("td"); + sink.tableCell(); - w.startElement("code"); + sink.monospaced(); boolean manyAssociation = false; @@ -338,101 +346,102 @@ private void writeFieldsTable(XMLWriter w, List fields, boolean elem String itemTagName = manyAssociation ? resolveTagName(tagName, xmlAssociationMetadata) : tagName; if (manyAssociation && xmlAssociationMetadata.isWrappedItems()) { - w.writeText(tagName); - w.writeMarkup("/"); + sink.text(tagName); + sink.rawText("/"); } if (isInnerAssociation(f)) { - w.startElement("a"); - w.addAttribute("href", "#" + getAnchorName(itemTagName, assoc.getToClass())); - w.writeText(itemTagName); - w.endElement(); + sink.link("#" + getAnchorName(itemTagName, assoc.getToClass())); + sink.text(itemTagName); + sink.link_(); } else if (ModelDefault.PROPERTIES.equals(f.getType())) { if (xmlAssociationMetadata.isMapExplode()) { - w.writeText("(key,value)"); + sink.text("(key,value)"); } else { - w.writeMarkup("key=value"); + sink.rawText("key=value"); } } else { - w.writeText(itemTagName); + sink.text(itemTagName); } if (manyAssociation) { - w.writeText("*"); + sink.text("*"); } } else { - w.writeText(tagName); + sink.text(tagName); } - w.endElement(); // code + sink.monospaced_(); - w.endElement(); // td + sink.tableCell_(); // Type column - w.startElement("td"); + sink.tableCell(); - w.startElement("code"); + sink.monospaced(); if (f instanceof ModelAssociation) { ModelAssociation assoc = (ModelAssociation) f; if (assoc.isOneMultiplicity()) { - w.writeText(assoc.getTo()); + sink.text(assoc.getTo()); } else { - w.writeText(assoc.getType().substring("java.util.".length())); + sink.text(assoc.getType().substring("java.util.".length())); if (assoc.isGenericType()) { - w.writeText("<" + assoc.getTo() + ">"); + sink.text("<" + assoc.getTo() + ">"); } } } else { - w.writeText(f.getType()); + sink.text(f.getType()); } - w.endElement(); // code + sink.monospaced_(); - w.endElement(); // td + sink.tableCell_(); // Since column if (showSinceColumn) { - w.startElement("td"); + sink.tableCell(); if (f.getVersionRange() != null) { Version fromVersion = f.getVersionRange().getFromVersion(); if (fromVersion != null && fromVersion.greaterThan(firstVersion)) { - w.writeMarkup(fromVersion.toString()); + sink.rawText(fromVersion.toString()); } } - w.endElement(); + sink.tableCell_(); } // Description column - w.startElement("td"); + sink.tableCell(); if (manyAssociation) { - w.writeMarkup("(Many) "); + sink.rawText("(Many) "); } - w.writeMarkup(getDescription(f)); + writeMarkupViaSink(sink, getDescription(f)); // Write the default value, if it exists. // But only for fields that are not a ModelAssociation if (f.getDefaultValue() != null && !(f instanceof ModelAssociation)) { - w.writeMarkup("

Default value: "); + sink.rawText("

Default value: "); - writeTextElement(w, "code", f.getDefaultValue()); + sink.monospaced(); + sink.text(f.getDefaultValue()); + sink.monospaced_(); - w.writeMarkup("

"); + sink.rawText("

"); } - w.endElement(); // td + sink.tableCell_(); - w.endElement(); // tr + sink.tableRow_(); } - w.endElement(); // table + sink.rawText("
"); } /** @@ -669,16 +678,8 @@ private static String getDescription(BaseElement element) { return (element.getDescription() == null) ? "No description." : rewrite(element.getDescription()); } - private static void writeTextElement(XMLWriter w, String name, String text) { - w.startElement(name); - w.writeText(text); - w.endElement(); - } - - private static void writeMarkupElement(XMLWriter w, String name, String markup) { - w.startElement(name); - w.writeMarkup(markup); - w.endElement(); + private static void writeMarkupViaSink(Sink sink, String markup) { + sink.rawText(markup); } /** diff --git a/modello-plugins/modello-plugin-xdoc/src/test/java/org/codehaus/modello/plugin/xdoc/XdocGeneratorTest.java b/modello-plugins/modello-plugin-xdoc/src/test/java/org/codehaus/modello/plugin/xdoc/XdocGeneratorTest.java index da8ac6d5f..bbead7c55 100644 --- a/modello-plugins/modello-plugin-xdoc/src/test/java/org/codehaus/modello/plugin/xdoc/XdocGeneratorTest.java +++ b/modello-plugins/modello-plugin-xdoc/src/test/java/org/codehaus/modello/plugin/xdoc/XdocGeneratorTest.java @@ -180,12 +180,13 @@ private void checkInternalLinks(String filename) throws Exception { Assert.assertTrue("should find some '