From 9029d0fc95a4a6ac37cbb9584816e26aeadf9c15 Mon Sep 17 00:00:00 2001 From: Mark Raynsford Date: Tue, 2 Jan 2024 14:49:05 +0000 Subject: [PATCH] Change the format of the xs command for better output. --- .../quarrel/ext/xstructural/QCommandXS.java | 376 ++++++++++++------ .../io7m/quarrel/tests/QApplicationTest.java | 29 +- 2 files changed, 291 insertions(+), 114 deletions(-) diff --git a/com.io7m.quarrel.ext.xstructural/src/main/java/com/io7m/quarrel/ext/xstructural/QCommandXS.java b/com.io7m.quarrel.ext.xstructural/src/main/java/com/io7m/quarrel/ext/xstructural/QCommandXS.java index 93f4a5c..c427450 100644 --- a/com.io7m.quarrel.ext.xstructural/src/main/java/com/io7m/quarrel/ext/xstructural/QCommandXS.java +++ b/com.io7m.quarrel.ext.xstructural/src/main/java/com/io7m/quarrel/ext/xstructural/QCommandXS.java @@ -69,6 +69,24 @@ public final class QCommandXS implements QCommandType private final boolean hidden; private final QCommandMetadata metadata; + private static final QParameterNamed1 TYPE = + new QParameterNamed1<>( + "--type", + List.of(), + new QConstant("The type of output."), + Optional.empty(), + String.class + ); + + private static final QParameterNamed1 PARAMETERS_INCLUDE_NAME = + new QParameterNamed1<>( + "--parameters-include", + List.of(), + new QConstant("The name of the file to include for parameters."), + Optional.of("parameters.xml"), + String.class + ); + /** * A command that produces an xstructural documentation template. * @@ -104,7 +122,7 @@ public QCommandMetadata metadata() @Override public List> onListNamedParameters() { - return List.of(); + return List.of(TYPE, PARAMETERS_INCLUDE_NAME); } @Override @@ -120,34 +138,37 @@ public QCommandStatus onExecute( { final var command = context.parametersPositionalRaw(); + final var type = + context.parameterValue(TYPE); final var result = QCommandTreeResolver.resolve(context.commandTree(), command); - if (result instanceof QResolutionRoot) { - return SUCCESS; - } - - if (result instanceof final QResolutionOKCommand cmd) { - showCommand(context, cmd.command()); - return SUCCESS; - } - - if (result instanceof final QResolutionOKGroup group) { - return SUCCESS; - } - - if (result instanceof QResolutionErrorDoesNotExist) { - return FAILURE; + switch (result) { + case final QResolutionRoot r -> { + return SUCCESS; + } + case final QResolutionOKCommand cmd -> { + showCommand(context, type, cmd.command()); + return SUCCESS; + } + case final QResolutionOKGroup group -> { + return SUCCESS; + } + case final QResolutionErrorDoesNotExist r -> { + return FAILURE; + } } - - return SUCCESS; } private static final String NS = "urn:com.io7m.structural:8:0"; + private static final String NS_XI = + "http://www.w3.org/2001/XInclude"; + private static void showCommand( final QCommandContextType context, + final String type, final QCommandType command) throws Exception { @@ -156,17 +177,36 @@ private static void showCommand( final var document = documents.newDocumentBuilder().newDocument(); - final Element root = - (Element) document.appendChild( - document.createElementNS(NS, "Section") - ); + switch (type) { + case "main" -> { + final Element root = + (Element) document.appendChild( + document.createElementNS(NS, "Section") + ); - root.setAttribute("title", command.metadata().name()); - root.setAttribute("id", idFor(command).toString()); + root.setAttribute("xmlns:xi", NS_XI); + root.setAttribute("title", command.metadata().name()); + root.setAttribute("id", idFor(command).toString()); - sectionName(context, command, document, root); - sectionDescription(context, command, document, root); - sectionExamples(document, root); + sectionName(context, command, document, root); + sectionDescription(context, command, document, root); + sectionExamples(document, root); + } + case "parameters" -> { + final Element root = + (Element) document.appendChild( + parameterTable( + context, + command, + document, + command.onListNamedParameters() + ) + ); + } + default -> { + throw new IllegalStateException("Unexpected value: " + type); + } + } write(document, context.output()); } @@ -176,20 +216,23 @@ private static void sectionExamples( final Element root) { final Element examples = - (Element) root.appendChild(document.createElementNS(NS, "Subsection")); + (Element) root.appendChild( + document.createElementNS(NS, "Subsection") + ); examples.setAttribute("title", "Examples"); final var formal = - (Element) examples.appendChild(document.createElementNS( - NS, - "FormalItem")); + (Element) examples.appendChild( + document.createElementNS(NS, "FormalItem") + ); formal.setAttribute("title", "Example"); formal.setAttribute("type", "example"); final var verbatim = - (Element) formal.appendChild(document.createElementNS( - NS, "Verbatim")); + (Element) formal.appendChild( + document.createElementNS(NS, "Verbatim") + ); verbatim.setTextContent("..."); } @@ -198,21 +241,24 @@ private static void sectionDescription( final QCommandType command, final Document document, final Element root) - throws QException { final Element description = - (Element) root.appendChild(document.createElementNS(NS, "Subsection")); + (Element) root.appendChild( + document.createElementNS(NS, "Subsection") + ); description.setAttribute("title", "Description"); final var para = - (Element) description.appendChild(document.createElementNS( - NS, - "Paragraph")); + (Element) description.appendChild( + document.createElementNS(NS, "Paragraph") + ); final var meta = command.metadata(); final var term = - (Element) para.appendChild(document.createElementNS(NS, "Term")); + (Element) para.appendChild( + document.createElementNS(NS, "Term") + ); term.setAttribute("type", "command"); term.setTextContent(meta.name()); @@ -223,77 +269,206 @@ private static void sectionDescription( final var named = command.onListNamedParameters(); if (!named.isEmpty()) { final var formal = - (Element) description.appendChild(document.createElementNS( - NS, - "FormalItem")); + (Element) description.appendChild( + document.createElementNS(NS, "FormalItem") + ); + formal.setAttribute("title", "Parameters"); + final var e = + (Element) description.appendChild( + document.createElementNS(NS_XI, "xi:include") + ); + + e.setAttribute("href", context.parameterValue(PARAMETERS_INCLUDE_NAME)); + formal.appendChild(e); + } + } + + private static Element parameterTable( + final QCommandContextType context, + final QCommandType command, + final Document document, + final List> named) + throws QException + { + final var root = + (Element) document.createElementNS(NS, "Subsection"); + + root.setAttribute("title", "Parameters"); + + final var sorted = new ArrayList<>(named); + sorted.sort(Comparator.comparing(QParameterType::name)); + + for (final var param : sorted) { + final var formal = + (Element) document.createElementNS(NS, "FormalItem"); + + formal.setAttribute("title", param.name()); + formal.setAttribute("id", UUID.nameUUIDFromBytes( + (command.metadata().name() + ":" + param.name()).getBytes(UTF_8) + ).toString()); + final var table = - (Element) formal.appendChild(document.createElementNS(NS, "Table")); + (Element) document.createElementNS(NS, "Table"); table.setAttribute("type", "parameterTable"); final var columns = - (Element) table.appendChild(document.createElementNS(NS, "Columns")); + (Element) table.appendChild( + document.createElementNS(NS, "Columns") + ); final var c0 = - (Element) columns.appendChild(document.createElementNS(NS, "Column")); - c0.setTextContent("Parameter"); + (Element) columns.appendChild( + document.createElementNS(NS, "Column") + ); + c0.setTextContent("Attribute"); final var c1 = - (Element) columns.appendChild(document.createElementNS(NS, "Column")); - c1.setTextContent("Type"); - - final var c2 = - (Element) columns.appendChild(document.createElementNS(NS, "Column")); - c2.setTextContent("Cardinality"); + (Element) columns.appendChild( + document.createElementNS(NS, "Column") + ); + c1.setTextContent("Value"); + + table.appendChild(generateRowForName(document, param)); + table.appendChild(generateRowForType(document, param)); + table.appendChild(generateRowForDefaults(context, document, param)); + table.appendChild(generateRowForCardinality(document, param)); + table.appendChild(generateRowForDescription(context, document, param)); + + formal.appendChild(table); + root.appendChild(formal); + } - final var c3 = - (Element) columns.appendChild(document.createElementNS(NS, "Column")); - c3.setTextContent("Default"); + return root; + } - final var c4 = - (Element) columns.appendChild(document.createElementNS(NS, "Column")); - c4.setTextContent("Description"); + private static Element generateRowForDescription( + final QCommandContextType context, + final Document document, + final QParameterNamedType param) + { + final var row = + (Element) document.createElementNS(NS, "Row"); + final var cellN = + (Element) document.createElementNS(NS, "Cell"); + final var cellV = + (Element) document.createElementNS(NS, "Cell"); + + final var vNode = + document.createTextNode(context.localize(param.description())); + + cellN.appendChild(document.createTextNode("Description")); + cellV.appendChild(vNode); + + row.appendChild(cellN); + row.appendChild(cellV); + return row; + } - final var sorted = new ArrayList<>(named); - sorted.sort(Comparator.comparing(QParameterType::name)); + private static Element generateRowForCardinality( + final Document document, + final QParameterNamedType param) + { + final var row = + (Element) document.createElementNS(NS, "Row"); + final var cellN = + (Element) document.createElementNS(NS, "Cell"); + final var cellV = + (Element) document.createElementNS(NS, "Cell"); + + final var vNode = + generateCellForCardinality(document, param); + + cellN.appendChild(document.createTextNode("Cardinality")); + cellV.appendChild(vNode); + + row.appendChild(cellN); + row.appendChild(cellV); + return row; + } - for (final var param : sorted) { - final var row = - (Element) table.appendChild(document.createElementNS(NS, "Row")); + private static Element generateRowForType( + final Document document, + final QParameterNamedType param) + { + final var row = + (Element) document.createElementNS(NS, "Row"); + final var cellN = + (Element) document.createElementNS(NS, "Cell"); + final var cellV = + (Element) document.createElementNS(NS, "Cell"); + + final var vNode = + document.createElementNS(NS, "Term"); + vNode.setAttribute("type", "class"); + vNode.setTextContent(param.type().getCanonicalName()); + + cellN.appendChild(document.createTextNode("Type")); + cellV.appendChild(vNode); + + row.appendChild(cellN); + row.appendChild(cellV); + return row; + } - generateCellForName(document, param, row); - generateCellForType(document, param, row); - generateCellForCardinality(document, param, row); - generateCellForDefault(context, document, param, row); - generateCellForDescription(context, document, param, row); - } - } + private static Element generateRowForName( + final Document document, + final QParameterNamedType param) + { + final var row = + (Element) document.createElementNS(NS, "Row"); + final var cellN = + (Element) document.createElementNS(NS, "Cell"); + final var cellV = + (Element) document.createElementNS(NS, "Cell"); + + final var vNode = + document.createElementNS(NS, "Term"); + vNode.setAttribute("type", "parameter"); + vNode.setTextContent(param.name()); + + cellN.appendChild(document.createTextNode("Name")); + cellV.appendChild(vNode); + + row.appendChild(cellN); + row.appendChild(cellV); + return row; } - private static void generateCellForDescription( + private static Element generateRowForDefaults( final QCommandContextType context, final Document document, - final QParameterNamedType param, - final Element row) + final QParameterNamedType param) + throws QException { - final var cell = - (Element) row.appendChild(document.createElementNS(NS, "Cell")); - cell.setTextContent(context.localize(param.description())); + final var row = + (Element) document.createElementNS(NS, "Row"); + final var cellN = + (Element) document.createElementNS(NS, "Cell"); + final var cellV = + (Element) document.createElementNS(NS, "Cell"); + + final var vNode = + generateTermForDefault(context, document, param); + + cellN.appendChild(document.createTextNode("Default Value")); + cellV.appendChild(vNode); + + row.appendChild(cellN); + row.appendChild(cellV); + return row; } - private static void generateCellForDefault( + private static Element generateTermForDefault( final QCommandContextType context, final Document document, - final QParameterNamedType param, - final Element row) + final QParameterNamedType param) throws QException { - final var cell = - (Element) row.appendChild(document.createElementNS(NS, "Cell")); final var term = - (Element) cell.appendChild(document.createElementNS(NS, "Term")); + (Element) document.createElementNS(NS, "Term"); final QValueConverterType c = (QValueConverterType) @@ -311,6 +486,8 @@ private static void generateCellForDefault( } else if (param instanceof final QParameterNamed1N n) { term.setTextContent(formatParameterCellContent1N(c, n)); } + + return term; } private static String formatParameterCellContent1N( @@ -373,15 +550,12 @@ private static String formatParameterCellContent1( return ""; } - private static void generateCellForCardinality( + private static Element generateCellForCardinality( final Document document, - final QParameterNamedType param, - final Element row) + final QParameterNamedType param) { - final var cell = - (Element) row.appendChild(document.createElementNS(NS, "Cell")); final var term = - (Element) cell.appendChild(document.createElementNS(NS, "Term")); + (Element) document.appendChild(document.createElementNS(NS, "Term")); term.setAttribute("type", "expression"); if (param instanceof QParameterNamed1) { @@ -393,32 +567,8 @@ private static void generateCellForCardinality( } else if (param instanceof QParameterNamed1N) { term.setTextContent("[1, N]"); } - } - private static void generateCellForType( - final Document document, - final QParameterNamedType param, - final Element row) - { - final var cell = - (Element) row.appendChild(document.createElementNS(NS, "Cell")); - final var term = - (Element) cell.appendChild(document.createElementNS(NS, "Term")); - term.setTextContent(param.type().getCanonicalName()); - term.setAttribute("type", "type"); - } - - private static void generateCellForName( - final Document document, - final QParameterNamedType param, - final Element row) - { - final var cell = - (Element) row.appendChild(document.createElementNS(NS, "Cell")); - final var term = - (Element) cell.appendChild(document.createElementNS(NS, "Term")); - term.setAttribute("type", "parameter"); - term.setTextContent(param.name()); + return term; } private static void sectionName( diff --git a/com.io7m.quarrel.tests/src/main/java/com/io7m/quarrel/tests/QApplicationTest.java b/com.io7m.quarrel.tests/src/main/java/com/io7m/quarrel/tests/QApplicationTest.java index 0200626..f04243b 100644 --- a/com.io7m.quarrel.tests/src/main/java/com/io7m/quarrel/tests/QApplicationTest.java +++ b/com.io7m.quarrel.tests/src/main/java/com/io7m/quarrel/tests/QApplicationTest.java @@ -595,7 +595,7 @@ public void testNameResolution0() } @Test - public void testXstructuralEverything() + public void testXstructuralMain() throws Exception { final var app = @@ -610,6 +610,33 @@ public void testXstructuralEverything() LOG, List.of( "_xs", + "--type", + "main", + "cmd-everything" + ) + ); + + assertNotEquals("", this.output.toString(UTF_8)); + } + + @Test + public void testXstructuralParameters() + throws Exception + { + final var app = + QApplication.builder(METADATA) + .setOutput(this.writer) + .addCommand(new QCommandXS("_xs", true)) + .addCommand(new QCommandParametersEverything()) + .build(); + + final var cmd = + app.run( + LOG, + List.of( + "_xs", + "--type", + "parameters", "cmd-everything" ) );