view xslt/model.xsl @ 8:5fe2e5724331

sync
author cin
date Wed, 07 Mar 2018 15:54:22 +0300
parents 3fe157be5141
children d3542662cf70
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 usefull 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:apply-templates mode="expand-member"/>
	</xsl:template>
	<xsl:template match="text()" mode="expand-member"/>
	
	<xsl:template match="*|text()" mode="expand-member-reference"/>
	
	<!-- short form of primaryKey{@name, @type} -->
	<xsl:template match="m:primaryKey" mode="expand-member">
		<xsl:variable name="specialProperty">
			<m:property special="true">
				<xsl:copy-of select="@*" />
				<m:hiddenBy name="{@name}" type="{@declaringType}"/>
				<xsl:copy-of select="*" />
			</m:property>
		</xsl:variable>
		<xsl:apply-templates select="exsl:node-set($specialProperty)" mode="expand-member"/>
		<xsl:apply-templates mode="expand-member"/>
	</xsl:template>
	
	<!-- long form of primaryKey{ member{@name}+, property{@name, @type} }  -->
	<xsl:template match="m:primaryKey[m:member | m:property]" mode="expand-member">
		<xsl:apply-templates mode="expand-member"/>
	</xsl:template>
	
	<!-- properties declared inside relations, they are @special -->
	<xsl:template match="m:primaryKey/m:property | m:thisKey/m:property" mode="expand-member">
		<xsl:variable name="specialProperty">
			<xsl:copy>
				<xsl:attribute name="special">true</xsl:attribute>
				<xsl:copy-of select="@*"/>
				<m:hiddenBy name="{@name}" type="{@declaringType}"/>
				<xsl:copy-of select="*"/>
			</xsl:copy>
		</xsl:variable>
		<xsl:apply-templates select="exsl:node-set($specialProperty)" mode="expand-member"/>
	</xsl:template>
	
	<!-- traverse inside referenced members -->
	<xsl:template match="m:member" mode="expand-member">
		<xsl:param name="declaringType"/>
		<xsl:variable name="expanded">
			<xsl:call-template name="getMembers">
				<xsl:with-param name="memberName" select="@name"/>
				<xsl:with-param name="type" select="$declaringType"/>
			</xsl:call-template>
		</xsl:variable>
		<!-- recusive expand -->
		<xsl:apply-templates select="exsl:node-set($expanded)" mode="expand-member-reference"/>
	</xsl:template>
	
	<xsl:template match="m:hasA/m:thisKey" mode="expand-member">
		<xsl:variable name="otherKey">
			<xsl:call-template name="getPrimaryKey">
				<xsl:with-param name="type" select="../@type"/>
			</xsl:call-template>
		</xsl:variable>
		<xsl:variable name="otherKeyExpanded">
			<xsl:apply-templates select="exsl:node-set($otherKey)" mode="expand-member"/>
		</xsl:variable>
		<xsl:variable name="pk" select="exsl:node-set($otherKeyExpanded)[1]"/>
		
		<m:property name="{@name}" type="{$pk/@type}" hidden="true">
			<m:related name="{$pk/@name}" type="{../@type}"/>
		</m:property>
	</xsl:template>
	
	<xsl:template match="m:thisKey[m:member | property]">
		<xsl:apply-templates mode="expand-member"/>
	</xsl:template>
	
</xsl:stylesheet>