view xslt/model.xsl @ 13:197a850b1f6f default tip

working version of xml2json transformation
author cin
date Mon, 09 Apr 2018 16:27:26 +0300
parents 19a8a71eaa54
children
line wrap: on
line source

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:m="http://implab.org/schemas/data-model.v1.xsd"
	xmlns:t="http://implab.org/schemas/temp" xmlns:exsl="http://exslt.org/common"
	exclude-result-prefixes="m xsl exsl">
	
	<xsl:import href="preprocess.xsl" />
	<xsl:include href="text-tools.xsl"/>
	
	<xsl:output method="xml" indent="yes" />

	<xsl:key name="type" match="m:package/m:*" use="@name" />

	<xsl:template match="/">
		<xsl:apply-templates select="$module" mode="document" />
	</xsl:template>

	<!-- code generation -->

	<!-- disable default template -->
	<xsl:template match="*|text()" mode="document" />

	<xsl:template match="t:module" mode="document">
		<xsl:apply-templates mode="document" />
	</xsl:template>

	
	
	<!-- member resolution traits -->
	
	<!-- Resolves members using given criteria
		@param type
			the name of the type whose members are inspected
		@param memberName
			the name of the member to lookup
		@param scope
			the scope where to look (members, properties, keys, relations)
			
		This template recursively inspects the given type down to it's inheritance
		hierarchy and outputs members matching criteria.
	 -->
	<xsl:template name="getMembers">
		<xsl:param name="type"/>
		<xsl:param name="memberName" select="''"/>
		<xsl:param name="scope" select="'members'"/>
		<!-- prevents cyclic references -->
		<xsl:param name="seen" select="/.."/>
		<xsl:for-each select="$module">
			<xsl:apply-templates select="key('type', $type)"
				mode="lookup-members">
				<xsl:with-param name="memberName" select="$memberName"/>
				<xsl:with-param name="scope" select="$scope"/>
				<xsl:with-param name="seen" select="$seen"/>
			</xsl:apply-templates>
		</xsl:for-each>
	</xsl:template>
	
	<xsl:template match="*" mode="lookup-members"/>
	
	<xsl:template match="m:entity" mode="lookup-members">
		<xsl:param name="memberName" select="''"/>
		<xsl:param name="scope" select="'members'"/>
		<xsl:param name="seen"/>
		
		<xsl:variable name="self" select="."/>
		
		<xsl:if test="not($seen[generate-id() = generate-id($self)])">
			<xsl:variable name="scopeMembers">
				<xsl:call-template name="selectScopeMembers">
					<xsl:with-param name="scope" select="$scope"/>
				</xsl:call-template>
			</xsl:variable>
			
			<xsl:variable name="members" select="exsl:node-set($scopeMembers)/*[not($memberName) or @name=$memberName]"/>
			
			<xsl:choose>
				<xsl:when test="$members">
					<xsl:copy-of select="$members"/>
				</xsl:when>
				<xsl:when test="m:extends">
					<xsl:call-template name="getMembers">
						<xsl:with-param name="type" select="m:extends/@type"/>
						<xsl:with-param name="memberName" select="$memberName"/>
						<xsl:with-param name="scope" select="$scope"/>
						<xsl:with-param name="seen" select="$seen | $self"/>
					</xsl:call-template>
				</xsl:when>
				<xsl:otherwise>
					<xsl:call-template name="warn">
						<xsl:with-param name="msg">
							<t:trace msg="failed to resolve {$memberName}, scope={$scope}"/>
							<t:trace msg="inspected classes">
							<xsl:for-each select="$seen | $self">
								<t:trace msg="{name()} {@name}"/>
							</xsl:for-each>
							</t:trace>
						</xsl:with-param>
					</xsl:call-template>
				</xsl:otherwise>
			</xsl:choose>
		</xsl:if>
	</xsl:template>
	
	<!-- this template implements scope filtering -->
	<xsl:template name="selectScopeMembers">
		<xsl:param name="scope" select="'members'"/>
		<xsl:choose>
			<xsl:when test="$scope='members'">
				<xsl:apply-templates mode="filter-members"/>
			</xsl:when>
			<xsl:when test="$scope='keys'">
				<xsl:apply-templates mode="filter-keys"/>
			</xsl:when>
			<xsl:when test="$scope='properties'">
				<xsl:apply-templates mode="fiflter-properties"/>
			</xsl:when>
			<xsl:when test="$scope='relations'">
				<xsl:apply-templates mode="fiflter-relations"/>
			</xsl:when>
		</xsl:choose>
	</xsl:template>
	
	<!-- filter-members -->
	<xsl:template match="*|text()" mode="filter-members"/>
	<xsl:template match="*[@name]" mode="filter-members">
		<xsl:copy>
			<xsl:copy-of select="@*" />
			<xsl:attribute name="declaringType"><xsl:value-of select="../@name" /></xsl:attribute>
			<xsl:copy-of select="*" />
		</xsl:copy>
	</xsl:template>
	
	<!-- filter-keys -->
	<xsl:template match="*|text()" mode="filter-keys"/>
	<xsl:template match="m:primaryKey" mode="filter-keys">
		<xsl:apply-templates select="." mode="filter-members"/>
	</xsl:template>
	
	<!-- filter-properties -->
	<xsl:template match="*|text()" mode="filter-properties"/>
	<xsl:template match="m:property" mode="filter-properties">
		<xsl:apply-templates select="." mode="filter-members"/>
	</xsl:template>
	
	<!-- filter-relations -->
	<xsl:template match="*|text()" mode="filter-relations"/>
	<xsl:template match="m:hasA | m:hasMany" mode="filter-relations">
		<xsl:apply-templates select="." mode="filter-members"/>
	</xsl:template>

	

	<!-- Resolves primaryKey information for the given type name.
		@returns m:primaryKey node copy with additional attribute
		@declaringType which points to the type where this primaryKey
		was defined.
	-->
	<xsl:template name="getPrimaryKey">
		<xsl:param name="type" />
		<xsl:call-template name="getMembers">
			<xsl:with-param name="type" select="$type"/>
			<xsl:with-param name="scope" select="'keys'"/>
		</xsl:call-template>
	</xsl:template>
	
	<!-- expand-member templates are used to process relations
	and keys and provide useful information. This mode is designed
	to recursively traverse members expanding details like special
	properties generated by relations.
	
	expand-member-reference is special mode used to traverse inside
	members which are referenced from relations. By default this mode
	creates deep copy of all elements.
	
	Use this templates to produce intermediate model for further
	processing by specific code-generators.
	
	@special - means that the member is a composite part and isn't
	accessible directly
	-->
	
	<xsl:template match="*" mode="expand-member">
		<xsl:copy>
			<xsl:copy-of select="@*"/>
			<xsl:apply-templates mode="expand-member"/>
		</xsl:copy>
	</xsl:template>
	
	
	<!-- short form of primaryKey{@name, @type} -->
	<xsl:template match="m:primaryKey[not(m:member)]" mode="expand-member">
		<xsl:variable name="pkName" select="concat(@declaringType, 'Pk')"/>
		<xsl:variable name="propName" select="@name" />
		
		<xsl:variable name="specialProperty">
			<m:property name="{$propName}" special="true">
				<xsl:copy-of select="@*[not(self::name)]" />
				<m:hiddenBy name="{$pkName}" type="{@declaringType}"/>
				<xsl:copy-of select="*" />
			</m:property>
		</xsl:variable>
		
		<xsl:apply-templates select="exsl:node-set($specialProperty)" mode="expand-member"/>
		<xsl:copy>
			<xsl:attribute name="name"><xsl:value-of select="$pkName"/></xsl:attribute>
			<xsl:attribute name="declarigType"><xsl:value-of select="@declaringType"/></xsl:attribute>
			<m:member name="{@name}" type="{@declaringType}"/>
		</xsl:copy>
	</xsl:template>
	
	<!-- long form of primaryKey{ member{@name}+ }  -->
	<!-- will be processed using default template -->
	
	<!-- short form of hasA  -->
	<xsl:template match="m:hasA" mode="expand-member">
		<xsl:variable name="thisPrefix" select="@name"/>
		
		<xsl:variable name="foreignPk">
			<xsl:call-template name="getPrimaryKey">
				<xsl:with-param name="type" select="@type"/>
			</xsl:call-template>
		</xsl:variable>
		<xsl:variable name="expandedPk">
			<xsl:apply-templates select="exsl:node-set($foreignPk)" mode="expand-member"/>
		</xsl:variable>
		<xsl:variable name="pkMembers">
			<xsl:apply-templates select="exsl:node-set($expandedPk)/*[not(@special)]" mode="resolve-members"/>
		</xsl:variable>
		<t:fk>
			<xsl:for-each select="exsl:node-set($pkMembers)/*">
				<t:thisKey name="{$thisPrefix}{@name}" matches="{@name}">
					<xsl:copy-of select="."/>
				</t:thisKey>
			</xsl:for-each>
		</t:fk>
	</xsl:template>
	
	<!-- short form of hasA with matched keys specified -->
	<!-- TODO -->
	<xsl:template match="m:hasA[m:thisKey]">
		<xsl:apply-templates select="m:thisKey" mode="generate-fk-properties"/>
		<xsl:copy>
			<xsl:copy-of select="@*"/>
			<xsl:apply-templates mode="expand-member"/>
		</xsl:copy>
	</xsl:template>
	
	<xsl:template match="m:hasA/m:thisKey" mode="expand-member">
		<m:member name="{@name}" matches="{@matches}" type="{../@declaringType}"/>
	</xsl:template>
	
	<!-- long form of hasA -->
	<xsl:template match="m:hasA[m:member]" mode="expand-member">
		<xsl:apply-templates mode="expand-member"/>
	</xsl:template>
	
	<!-- resolve-members -->
	<!-- processes members and copies members to output -->
	<!-- TODO: Possible recursion -->
	<xsl:template match="m:member" mode="resolve-members">
		<xsl:variable name="name" select="@name"/>
		<xsl:variable name="declaringType" select="@type" />
		<xsl:choose>
			<xsl:when test="/*[@name=$name and @declaringType=$declaringType]">
				<!-- when reference points to a locally defined member  -->
				<!-- locally defined members are already expanded -->
				<xsl:apply-templates select="/*[@name=$name and @declaringType=$declaringType]" mode="resolve-members"/>
			</xsl:when>
			<xsl:otherwise>
				<!-- otherwise try to use getMembers, then expand it and resolve members  -->
				<xsl:variable name="members">
					<xsl:call-template name="getMembers">
						<xsl:with-param name="memberName" select="$name"/>
						<xsl:with-param name="type" select="$declaringType"/>
					</xsl:call-template>
				</xsl:variable>
				<!-- expand resolved members like 'hasA' and 'thisKey' -->
				<xsl:variable name="expanded">
					<xsl:apply-templates select="exsl:node-set($members)" mode="resolve-members"/>
				</xsl:variable>
				<!-- skip @special members when resolving -->
				<xsl:apply-templates select="exsl:node-set($expanded)/*[not(@special)]" mode="resolve-members"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>
	
	<xsl:template match="m:property" mode="resolve-members">
		<xsl:copy-of select="."/>
	</xsl:template>
	
</xsl:stylesheet>