Skip to content

Commit

Permalink
#39, #53, usnistgov/OSCAL#633. JSON->supermodel conversion. Metapath …
Browse files Browse the repository at this point in the history
…mapping corrections. Nearly working round-trip test conversion.
  • Loading branch information
wendellpiez authored and david-waltermire committed Dec 21, 2020
1 parent 1498811 commit d0d8cef
Show file tree
Hide file tree
Showing 10 changed files with 579 additions and 98 deletions.
2 changes: 1 addition & 1 deletion toolchains/xslt-M4/compose/unfold-model-map.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<xsl:template match="*[exists(@group-name)]">
<group name="{@group-name}" in-xml="{ if (@group-xml='GROUPED') then 'SHOWN' else 'HIDDEN' }"
max-occurs="1" min-occurs="{ if (@min-occurs='0') then '0' else '1'}">
<xsl:copy-of select="@json-key-flag | @group-json"/>
<xsl:copy-of select="@json-key-flag | @group-json | @recursive"/>
<xsl:next-match/>
</group>
</xsl:template>
Expand Down
17 changes: 14 additions & 3 deletions toolchains/xslt-M4/converter-gen/produce-json-converter.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@
<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:try select="m:jsonize-path($matching-xml)">
<xsl:catch>
<xsl:message expand-text="true" xmlns:err="http://www.w3.org/2005/xqt-errors">(On { @name }) { $matching-xml }: { $err:description }</xsl:message>
</xsl:catch>
</xsl:try>-->
<xsl:text expand-text="true"><!--(: { $matching-xml} :) -->{ m:jsonize-path($matching-xml) }</xsl:text>
<!--<xsl:sequence select="m:jsonize-path($matching-xml)"/>-->

</xsl:template>
Expand All @@ -71,7 +76,7 @@

<!-- Overriding interface template -->
<xsl:template match="*" mode="make-pull">
<pull who="{name()}[@key='{@key}']"/>
<XSLT:apply-templates select="*[@key='{@key}']"/>
<!--<pull>
<xsl:copy>
<xsl:copy-of select="@*"/>
Expand All @@ -80,6 +85,12 @@
<!--<xsl:apply-templates select="." mode="make-xml-pull"/>-->
</xsl:template>


<xsl:template name="comment-template">
<xsl:comment expand-text="true">
<xsl:text> Cf XML match="</xsl:text>
<xsl:apply-templates select="." mode="make-xml-match"/>
<xsl:text>" </xsl:text>
</xsl:comment>
</xsl:template>

</xsl:stylesheet>
74 changes: 42 additions & 32 deletions toolchains/xslt-M4/converter-gen/produce-xml-converter.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -57,24 +57,25 @@
<!-- first we produce templates for (each) of the global definitions.-->
<xsl:for-each-group select="//*[@scope = 'global'][not(@recursive='true')]"
group-by="string-join((local-name(), @name), ':')">
<!-- These are all the same so we do only one -->
<xsl:apply-templates select="current-group()[1]" mode="make-template">
<xsl:with-param name="ilk" select="current-group()"/>
<!-- These are all the same so we do only one, but we pass in the group to construct the match -->
<xsl:apply-templates select="current-group()[1]" mode="make-template-for-global">
<xsl:with-param name="team" tunnel="true" select="current-group()"/>
</xsl:apply-templates>
</xsl:for-each-group>

<!-- next we produce templates for local definitions -->
<!-- next we produce templates for local definitions - a receiving template
filters out the globals ... -->
<xsl:apply-templates select=".//assembly | .//field | .//flag" mode="make-template-for-local"/>

<!--finally, if needed, a template for copying prose across, stripping ns -->
<xsl:call-template name="cast-prose-template"/>
<xsl:call-template name="for-this-converter"/>

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


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

<xsl:template name="make-strip-space">
Expand All @@ -84,8 +85,20 @@
<xsl:template name="initial-comment">
<xsl:comment> METASCHEMA conversion stylesheet supports XML -> METASCHEMA/SUPERMODEL conversion </xsl:comment>
</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 name="cast-prose-template">
<xsl:template name="for-this-converter">
<!-- For the XML converter, we need a generic template to cast prose contents into the supermodel namespace -->
<xsl:if test="exists(//value[@as-type = ('markup-line', 'markup-multiline')])">
<XSLT:template match="*" mode="cast-prose">
<XSLT:element name="{{ local-name() }}"
Expand All @@ -97,18 +110,16 @@
</xsl:if>
</xsl:template>



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

<xsl:template match="*" mode="make-template-for-local">
<xsl:apply-templates select="." mode="make-template"/>
<xsl:apply-templates select="." mode="make-template-for-global"/>
</xsl:template>

<!-- no template for implicit wrappers on markup-multiline -->
<xsl:template priority="2" match="field[empty(@gi)][value/@as-type='markup-multiline']" mode="make-template"/>
<xsl:template priority="2" match="field[empty(@gi)][value/@as-type='markup-multiline']" mode="make-template-for-global"/>

<xsl:template match="*" mode="make-template">
<xsl:template match="*" mode="make-template-for-global">
<xsl:variable name="matching">
<xsl:apply-templates select="." mode="make-match"/>
</xsl:variable>
Expand All @@ -117,6 +128,7 @@
<!-- A parameter allows the call to drop the key, necessary for recursive
groups of elements also allowed at the root (at least) -->
<XSLT:param name="with-key" select="true()"/>
<xsl:call-template name="comment-template"/>
<xsl:element name="{ local-name() }" namespace="http://csrc.nist.gov/ns/oscal/metaschema/1.0/supermodel">
<xsl:copy-of select="@* except (@key|@scope)"/>
<!--'SCALAR' marks fields that will be strings or data values, not maps (objects)
Expand All @@ -136,39 +148,34 @@
</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:apply-templates select="parent::group[exists(@gi)]" mode="make-template-for-global"/>
</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:template match="flag" mode="make-template-for-global">
<xsl:variable name="matching">
<xsl:apply-templates select="." mode="make-match"/>
</xsl:variable>
<XSLT:template match="{ $matching}">
<xsl:call-template name="comment-template"/>
<flag>
<xsl:copy-of select="@* except @scope"/>
<XSLT:value-of select="."/>
<XSLT:value-of select="."/>
</flag>
</XSLT:template>
</xsl:template>

<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:param name="team" tunnel="true" select="."/>
<xsl:variable name="team-matches" as="xs:string*">
<xsl:for-each select="$team">
<xsl:value-of>
<xsl:apply-templates select=".." mode="make-xml-match"/>
<xsl:text>/@</xsl:text>
<xsl:value-of select="@gi"/>
</xsl:value-of>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="distinct-values($team-matches)" separator=" | "/>
</xsl:template>

<xsl:template priority="11" match="flag" mode="make-xml-step">
Expand Down Expand Up @@ -284,5 +291,8 @@
<XSLT:value-of select="."/>
</xsl:template>

<!-- stub for override -->
<xsl:template name="comment-template"/>


</xsl:stylesheet>
34 changes: 21 additions & 13 deletions toolchains/xslt-M4/metapath/metapath-jsonize.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
exclude-result-prefixes="#all"
expand-text="true">

<xsl:output indent="yes"/>
<!-- xmlns:p="xpath20" -->
<!--<xsl:import href="REx/xpath20.xslt"/>-->

Expand Down Expand Up @@ -60,13 +61,15 @@
<!--<test>field-flagged-groupable</test>
<test>field-dynamic-value-key</test>-->
<!--<test>*/@color</test>-->
<test>field-by-key/@id</test>
<test>field-dynamic-value-key/@id</test>
<test>wrapped-assemblies</test>
<test>EVERYTHING/@id|field-named-value/@id</test>

<!--<test>field-dynamic-value-key/@id</test>
<test>field-1only/(value() + path)</test>
<test>/EVERYTHING/(field-1only | field-named-value)</test>
<test>/*</test>
<test>/*</test>-->
<!--<test>field-named-value/value()</test>-->

<!--field with dynamic value key?-->
Expand Down Expand Up @@ -97,12 +100,12 @@

<xsl:template match="test" mode="testing">
<xsl:variable name="map" select="m:path-map(string(.))"/>
<test expr="{.}" expand="{string($map)}" json-path="{m:jsonize-path(.)}">
<test expr="{.}" expand="{string-join($map)}" json-path="{m:jsonize-path(.)}">
<!--<test>-->
<!--<xsl:apply-templates select="key('obj-by-gi',.,$definition-map)" mode="cast-node-test"/>-->
<!--<xsl:sequence select="m:jsonize-path(.)"/>
<xsl:sequence select="m:path-map(.)"/>-->
<!--<xsl:sequence select="p:parse-XPath(.)"/>-->
<!--<xsl:sequence select="m:jsonize-path(.)"/>-->
<xsl:sequence select="m:path-map(.)"/>
<xsl:sequence select="p:parse-XPath(.)"/>
</test>
</xsl:template>

Expand All @@ -118,9 +121,14 @@
<cast>{$px}:array[@key='{../@key}']/{$px}:map</cast>
</xsl:template>

<!-- Catches groups that are explicit in the XML -->
<xsl:template match="group[exists(@gi)]" mode="cast-node-test">
<cast>{$px}:array[@key='{@key}']</cast>
</xsl:template>

<!-- Catches assembly grouped as SINGLETON_OR_ARRAY -->
<xsl:template match="group/assembly" mode="cast-node-test">
<cast>{$px}:assembly[@key='{../@key}']/{$px}:map</cast>
<cast>{$px}:array[@key='{../@key}']/{$px}:map</cast>
<cast>{$px}:map[@key='{../@key}']</cast>
</xsl:template>

Expand Down Expand Up @@ -220,11 +228,11 @@
<xsl:variable name="not-value-filter">
<xsl:text expand-text="true">[not(@key=({ string-join($not-value-flags ! ( '''' || . || ''''), ',' ) }))]</xsl:text>
</xsl:variable>
<cast>{$px}:{$value-type}{ $not-value-filter[matches(.,'\S')]}</cast>
<cast>{$px}:{$value-type}{ $not-value-filter[matches(.,'\S')]}/@key</cast>
</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 @@ -267,7 +275,7 @@

<xsl:template match="@as-type[. = $numeric-types]" mode="json-type">number</xsl:template>

<xsl:function name="m:path-map" as="node()">
<xsl:function name="m:path-map" as="node()*">
<xsl:param name="expr"/>
<xsl:variable name="parse.tree" select="p:parse-XPath($expr)"/>
<xsl:apply-templates select="$parse.tree" mode="path-map"/>
Expand All @@ -288,11 +296,11 @@
</xsl:function>

<!-- m:jsonization returns the path mapped step by step into JSON, as an XML element tree -->
<xsl:function name="m:jsonization" as="element()">
<xsl:function name="m:jsonization" as="node()*">
<xsl:param name="metapath" as="xs:string" required="yes"/>
<xsl:variable name="path-map" select="m:path-map($metapath)"/>
<xsl:choose>
<xsl:when test="$path-map instance of element()">
<xsl:when test="exists($path-map/*)">
<xsl:apply-templates select="$path-map" mode="cast-path"/>
</xsl:when>
<xsl:otherwise>
Expand Down
Loading

0 comments on commit d0d8cef

Please sign in to comment.