view xslt/model.xsl @ 9:d3542662cf70

working on members preprocessing
author cin
date Sun, 11 Mar 2018 10:27:32 +0300
parents 5fe2e5724331
children 19a8a71eaa54
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:clr="http://implab.org/schemas/data-model/dotnet.v1.xsd"
	xmlns:cs="http://implab.org/schemas/code-dom/csharp.v1.xsd" xmlns:sql="http://implab.org/schemas/data-model/sql.v1.xsd"
	xmlns:t="http://implab.org/schemas/temp" xmlns:exsl="http://exslt.org/common"
	exclude-result-prefixes="clr m xsl exsl sql">
	
	<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="/">
		<cs:document>
			<xsl:apply-templates select="$module" mode="document" />
		</cs: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
	does nothing.
	
	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" 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>
			<m:member name="{@name}" type="{@declaringType}"/>
		</xsl:copy>
	</xsl:template>
	
	<!-- long form of primaryKey{ member{@name}+ }  -->
	<xsl:template match="m:primaryKey[m:member]" mode="expand-member">
		<xsl:apply-templates mode="expand-member"/>
	</xsl:template>
	
	<!-- short form of hasA  -->
	<xsl:template match="m:hasA" mode="expand-member">
		<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="resolvedPk">
			<xsl:apply-templates select="exsl:node-set($expandedPk)/m:primaryKey" mode="resolve-members"/>
		</xsl:variable>
	</xsl:template>
	
	<!-- short form of hasA with matched keys specified -->
	<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}"/>
	</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 expands -->
	
	<xsl:template match="m:member" mode="resolve-members">
		<xsl:variable name="$name" select="@name"/>
		<xsl:variable name="$declaringType" select="@declaringType" />
		
		<xsl:choose>
			<xsl:when test="/*[@name=$name and @declaringType=$declaringType]">
				<xsl:apply-templates select="/*[@name=$name and @declaringType=$declaringType]" mode="resolve-members"/>
			</xsl:when>
			<xsl:otherwise>
				<xsl:variable name="fragment">
					<xsl:call-template name="getMembers">
						<xsl:with-param name="memberName" select="$name"/>
						<xsl:with-param name="type" select="$declaringType"/>
					</xsl:call-template>
				</xsl:variable>
				<xsl:apply-templates select="exsl:node-set($fragment)/*[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>