Skip to content

Commit

Permalink
Building out JSON -> supermodel conversion; bug fixes in Metapath map…
Browse files Browse the repository at this point in the history
…ping
  • Loading branch information
wendellpiez authored and david-waltermire committed Dec 21, 2020
1 parent cdbacce commit 1498811
Show file tree
Hide file tree
Showing 17 changed files with 1,513 additions and 288 deletions.
690 changes: 576 additions & 114 deletions issue39-refactor.xpr

Large diffs are not rendered by default.

85 changes: 85 additions & 0 deletions toolchains/xslt-M4/converter-gen/produce-json-converter.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:m="http://csrc.nist.gov/ns/oscal/metaschema/1.0"
xmlns:XSLT="http://csrc.nist.gov/ns/oscal/metaschema/xslt-alias"

xpath-default-namespace="http://csrc.nist.gov/ns/oscal/metaschema/1.0"
exclude-result-prefixes="xs math"
version="3.0"
xmlns="http://csrc.nist.gov/ns/oscal/metaschema/1.0/supermodel">

<!-- Purpose: Produce an XSLT for converting JSON valid to a Metaschema model, to its supermodel equivalent. -->
<!-- Input: A Metaschema definition map -->
<!-- Output: An XSLT -->

<!-- Most of the logic is the same as the XML converter. -->
<xsl:import href="produce-xml-converter.xsl"/>

<!-- Path conversion logic is here -->
<xsl:import href="../metapath/metapath-jsonize.xsl"/>

<xsl:output indent="yes"/>

<xsl:namespace-alias stylesheet-prefix="XSLT" result-prefix="xsl"/>

<!-- $px is the prefix to be used on names in the XPath JSON -->
<xsl:param name="px" as="xs:string">j</xsl:param>

<!-- The definition map for tracing path traversals is the source document -->
<xsl:param name="definition-map" select="/"/>

<!-- Overriding the root template in metapath-jsonize.xsl (which drives testing) -->
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>

<xsl:template name="xpath-namespace">
<!--<xsl:attribute name="xpath-default-namespace">http://www.w3.org/2005/xpath-functions</xsl:attribute>-->
<xsl:namespace name="{$px}">http://www.w3.org/2005/xpath-functions</xsl:namespace>
</xsl:template>

<xsl:template name="make-strip-space">
<XSLT:strip-space elements="{$px}:map {$px}:array"/>
</xsl:template>

<xsl:template name="initial-comment">
<xsl:comment> METASCHEMA conversion stylesheet supports JSON -> METASCHEMA/SUPERMODEL conversion </xsl:comment>
</xsl:template>

<!-- Overriding the imported template for casting prose elements (wrt namespace) since we do not need it -->
<xsl:template name="cast-prose-template"/>

<!-- Overriding interface template -->
<xsl:template match="*" mode="make-match" as="xs:string">
<xsl:variable name="matching-xml">
<xsl:apply-templates select="." mode="make-xml-match"/>
</xsl:variable>
<xsl:text expand-text="true">(: { $matching-xml} :) { m:jsonize-path($matching-xml) }</xsl:text>
<!--<xsl:sequence select="m:jsonize-path($matching-xml)"/>-->

</xsl:template>

<!-- Overriding interface template -->
<xsl:template match="*" mode="make-step" as="xs:string">
<xsl:variable name="step-xml">
<xsl:apply-templates select="." mode="make-xml-step"/>
</xsl:variable>
<xsl:sequence select="m:jsonize-path($step-xml)"/>
</xsl:template>

<!-- Overriding interface template -->
<xsl:template match="*" mode="make-pull">
<pull who="{name()}[@key='{@key}']"/>
<!--<pull>
<xsl:copy>
<xsl:copy-of select="@*"/>
</xsl:copy>
</pull>-->
<!--<xsl:apply-templates select="." mode="make-xml-pull"/>-->
</xsl:template>



</xsl:stylesheet>
128 changes: 83 additions & 45 deletions toolchains/xslt-M4/converter-gen/produce-xml-converter.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,28 @@
<xsl:variable name="source-namespace" select="string(/*/@namespace)"/>
<xsl:variable name="source-prefix" select="string(/*/@prefix)"/>

<!-- Interface template for override -->
<xsl:template match="*" mode="make-match" as="xs:string">
<xsl:apply-templates select="." mode="make-xml-match"/>
</xsl:template>

<!-- Interface template for override -->
<xsl:template match="*" mode="make-step" as="xs:string">
<xsl:apply-templates select="." mode="make-xml-step"/>
</xsl:template>

<!-- Interface template for override -->
<xsl:template match="*" mode="make-pull">
<xsl:apply-templates select="." mode="make-xml-pull"/>
</xsl:template>

<xsl:template match="/model">
<XSLT:stylesheet version="3.0" xpath-default-namespace="{ $source-namespace }"
<XSLT:stylesheet version="3.0"
exclude-result-prefixes="#all">

<XSLT:strip-space elements="{distinct-values(//assembly/@gi)}"/>
<xsl:call-template name="xpath-namespace"/>
<xsl:call-template name="make-strip-space"/>

<xsl:comment> METASCHEMA conversion stylesheet supports XML -> METASCHEMA/SUPERMODEL conversion </xsl:comment>
<xsl:call-template name="initial-comment"/>
<xsl:text>&#xA;</xsl:text>
<xsl:comment> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </xsl:comment>
<xsl:text>&#xA;</xsl:text>
Expand All @@ -52,18 +67,38 @@
<xsl:apply-templates select=".//assembly | .//field | .//flag" mode="make-template-for-local"/>

<!--finally, if needed, a template for copying prose across, stripping ns -->
<xsl:if test="exists(//value[@as-type=('markup-line','markup-multiline')])">
<xsl:call-template name="cast-prose-template"/>

</XSLT:stylesheet>
</xsl:template>


<xsl:template name="xpath-namespace">
<xsl:attribute name="xpath-default-namespace" expand-text="true">{ $source-namespace }</xsl:attribute>
</xsl:template>

<xsl:template name="make-strip-space">
<XSLT:strip-space elements="{distinct-values(//assembly/@gi)}"/>
</xsl:template>

<xsl:template name="initial-comment">
<xsl:comment> METASCHEMA conversion stylesheet supports XML -> METASCHEMA/SUPERMODEL conversion </xsl:comment>
</xsl:template>

<xsl:template name="cast-prose-template">
<xsl:if test="exists(//value[@as-type = ('markup-line', 'markup-multiline')])">
<XSLT:template match="*" mode="cast-prose">
<XSLT:element name="{{ local-name() }}" namespace="http://csrc.nist.gov/ns/oscal/metaschema/1.0/supermodel">
<XSLT:element name="{{ local-name() }}"
namespace="http://csrc.nist.gov/ns/oscal/metaschema/1.0/supermodel">
<XSLT:copy-of select="@*"/>
<XSLT:apply-templates mode="#current"/>
<XSLT:apply-templates mode="#current"/>
</XSLT:element>
</XSLT:template>
</xsl:if>

</XSLT:stylesheet>
</xsl:if>
</xsl:template>



<xsl:template match="*[@scope='global']" mode="make-template-for-local"/>

<xsl:template match="*" mode="make-template-for-local">
Expand All @@ -75,7 +110,7 @@

<xsl:template match="*" mode="make-template">
<xsl:variable name="matching">
<xsl:apply-templates select="." mode="make-xml-match"/>
<xsl:apply-templates select="." mode="make-match"/>
</xsl:variable>
<xsl:variable name="json-key-flag-name" select="@json-key-flag"/>
<XSLT:template match="{ $matching}">
Expand All @@ -96,22 +131,28 @@
</XSLT:attribute>
</XSLT:if>
</xsl:if>
<xsl:for-each select="parent::model" expand-text="true">
<XSLT:if test=". is /*">
<XSLT:attribute name="namespace">{ $source-namespace }</XSLT:attribute>
<!-- don't need unless we have a requirement to prefix in serialization: <XSLT:attribute name="prefix">{ $source-prefix }</XSLT:attribute>-->
</XSLT:if>
</xsl:for-each>
<xsl:call-template name="provide-namespace"/>
<xsl:apply-templates select="*" mode="make-pull"/>
</xsl:element>
</XSLT:template>
<!--Additionally we need templates for elements defined implicitly as wrappers for given assemblies or fields-->
<xsl:apply-templates select="parent::group[exists(@gi)]" mode="make-template"/>
</xsl:template>

<xsl:template name="provide-namespace">
<!-- iff at the top -->
<xsl:for-each select="parent::model" expand-text="true">
<!-- likewise the XSLT provides a namespace only if at the top -->
<XSLT:if test=". is /*">
<XSLT:attribute name="namespace">{ $source-namespace }</XSLT:attribute>
<!-- don't need unless we have a requirement to prefix in serialization: <XSLT:attribute name="prefix">{ $source-prefix }</XSLT:attribute>-->
</XSLT:if>
</xsl:for-each>
</xsl:template>

<xsl:template match="flag" mode="make-template">
<xsl:variable name="matching">
<xsl:apply-templates select="." mode="make-xml-match"/>
<xsl:apply-templates select="." mode="make-match"/>
</xsl:variable>
<XSLT:template match="{ $matching}">
<flag>
Expand All @@ -121,9 +162,17 @@
</XSLT:template>
</xsl:template>

<xsl:template priority="11" match="flag[@scope='global']" mode="make-xml-match make-xml-step">
<xsl:text>@</xsl:text>
<xsl:value-of select="@gi"/>
<xsl:template priority="11" match="flag" mode="make-xml-match">
<xsl:value-of>
<xsl:apply-templates select=".." mode="make-xml-match"/>
<xsl:text>/</xsl:text>
<xsl:value-of select="'@' || @gi"/>
</xsl:value-of>

</xsl:template>

<xsl:template priority="11" match="flag" mode="make-xml-step">
<xsl:value-of select="'@' || @gi"/>
</xsl:template>

<xsl:template priority="10" match="*[@scope='global'] | group[*/@scope='global']" mode="make-xml-match make-xml-step">
Expand All @@ -134,17 +183,16 @@
<xsl:value-of select="@gi"/>
</xsl:template>

<!-- Matches local declarations (only) -->
<!-- Matches local declarations (only) -->
<xsl:template match="*" mode="make-xml-match">

<xsl:for-each select="ancestor-or-self::*[@gi] except ancestor-or-self::*[@scope='global']/ancestor::*">
<xsl:if test="position() gt 1">/</xsl:if>
<xsl:value-of select="self::flag/'@'"/>
<xsl:apply-templates select="." mode="make-xml-step"/>
</xsl:for-each>
</xsl:template>

<xsl:template match="*" mode="make-pull">
<!-- fallback should never match -->
<xsl:template match="*" mode="make-xml-pull make-json-pull">
<pull who="{local-name()}">
<xsl:copy-of select="@gi | @key"/>
</pull>
Expand All @@ -153,7 +201,7 @@
<xsl:variable name="prose-elements">p | ul | ol | pre | h1 | h2 | h3 | h4 | h5 | h6 | table</xsl:variable>

<!-- A field without a GI is implicit in the XML; Metaschema prevents it from having flags -->
<xsl:template priority="2" match="field[empty(@gi)][value/@as-type='markup-multiline']" mode="make-pull">
<xsl:template priority="2" match="field[empty(@gi)][value/@as-type='markup-multiline']" mode="make-xml-pull">
<XSLT:for-each-group select="{ $prose-elements }" group-by="true()">
<field in-json="SCALAR">
<xsl:copy-of select="@* except @scope"/>
Expand All @@ -165,15 +213,16 @@
</XSLT:for-each-group>
</xsl:template>

<xsl:template match="flag" mode="make-pull">
<XSLT:apply-templates select="@{@gi}"/>
</xsl:template>

<xsl:template match="field | assembly | group[exists(@gi)]" mode="make-pull">
<XSLT:apply-templates select="{@gi}"/>
<xsl:template match="flag | field | assembly | group[exists(@gi)]" mode="make-xml-pull">
<xsl:variable name="path">
<xsl:apply-templates select="." mode="make-xml-step"/>
</xsl:variable>
<XSLT:apply-templates select="{$path}"/>
</xsl:template>

<xsl:template match="group" mode="make-pull">
<!-- A group with no @gi does not appear as an element in the XML source, so when
sourcing from XML, we have to group its children as a group. -->
<xsl:template match="group" mode="make-xml-pull">
<xsl:variable name="json-grouping" select="if (exists(@group-json)) then @group-json else 'SINGLETON_OR_ARRAY'"/>
<XSLT:for-each-group select="{ */@gi }" group-by="true()">
<group in-json="{ $json-grouping }">
Expand All @@ -186,18 +235,7 @@
</XSLT:for-each-group>
</xsl:template>


<!--<xsl:template match="field | assembly" mode="make-pull">
<XSLT:for-each select="{@gi}">
<xsl:element name="{ local-name() }"
namespace="http://csrc.nist.gov/ns/oscal/metaschema/1.0/supermodel">
<xsl:copy-of select="@gi | @key"/>
<xsl:apply-templates mode="#current"/>
</xsl:element>
</XSLT:for-each>
</xsl:template>-->

<xsl:template match="value" mode="make-pull">
<xsl:template match="value" mode="make-xml-pull">
<value>
<xsl:copy-of select="@key | @key-flag | @as-type"/>
<xsl:apply-templates select="@as-type" mode="json-type"/>
Expand Down
10 changes: 4 additions & 6 deletions toolchains/xslt-M4/metapath/metapath-jsonize.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@

<!-- field points to its object -->
<xsl:template match="field" mode="cast-node-test">
<cast>{$px}:map</cast>
<cast>{$px}:map{ @key ! ('[@key=''' || . || ''']') }</cast>
</xsl:template>

<!-- sometimes the field value sits on a dynamic flag -->
Expand All @@ -195,7 +195,7 @@
<!-- otherwise a field value points to its value property -->
<xsl:template match="field" mode="cast-value-path">
<xsl:variable name="type">
<xsl:apply-templates select="." mode="object-type"/>
<xsl:apply-templates select="value" mode="object-type"/>
</xsl:variable>
<cast>{$px}:{$type}[@key='{value/@key}']</cast>
</xsl:template>
Expand Down Expand Up @@ -224,7 +224,7 @@
</xsl:template>

<xsl:template match="*" mode="cast-node-test" expand-text="true">
<cast>OoopsFellThroughOn{ name() }</cast>
<!--<cast>OoopsFellThroughOn{ name() }</cast>-->
<cast>{$px}:*[@key='{../@key}']</cast>
</xsl:template>

Expand Down Expand Up @@ -296,11 +296,9 @@
<xsl:apply-templates select="$path-map" mode="cast-path"/>
</xsl:when>
<xsl:otherwise>
<ERROR xsl:expand-text="true">(: PARSING '{$metapath}' RETURNS {$path-map => normalize-space() } :)</ERROR>
<ERROR xsl:expand-text="true">(: PARSING '{$metapath}' RETURNS { normalize-space($path-map) } :)</ERROR>
</xsl:otherwise>
</xsl:choose>


</xsl:function>


Expand Down
Loading

0 comments on commit 1498811

Please sign in to comment.