<?xml version="1.0" encoding="UTF-8" ?> <!-- Application Description Language framework adl2canonical.xsl (c) 2007 Simon Brooke Transform ADL into a canonical form, expanding and making explicit things left implicit in the manually maintained form. Specifically, in the canonicalised form (i) every entity element has a key subelement. If it did not have one in the supplied ADL, then a formal primary key is auto generated (ii) every form, page or list has properties='listed'; where the supplied ADL had properties='all' or properties='user-distinct' the correct fields are generated. (iii) TODO: all permissions are fully specified down to field level, by inheriting where necessary. For every group specified in the ADL, for every entity, form, page, list or field, the canonical form should explicitly state the permission, even if it is 'none'. $Author: simon $ $Revision: 1.10 $ $Date: 2010-07-20 19:53:40 $ --> <xsl:stylesheet version="1.0" xmlns="http://bowyer.journeyman.cc/adl/1.4.1/" xmlns:adl="http://bowyer.journeyman.cc/adl/1.4.1/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="adl"> <xsl:output encoding="UTF-8" indent="yes" method="xml" /> <!-- The convention to use for naming auto-generated abstract primary keys. Known values are Id - the autogenerated primary key, if any, is called just 'Id' Name - the autogenerated primary key has the same name as the entity NameId - the name of the auto generated primary key is the name of the entity followed by 'Id' Name_Id - the name of the auto generated primary key is the name of the entity followed by '_Id' --> <xsl:param name="abstract-key-name-convention" select="Id"/> <!-- the name and version of the product being built --> <xsl:param name="product-version" select="'Application Description Language Framework'"/> <!-- a prefix to prepend to all tablenames --> <xsl:param name="tablename-prefix"/> <xsl:template match="adl:application"> <xsl:copy> <xsl:attribute name="name"> <xsl:value-of select="@name"/> </xsl:attribute> <xsl:if test="@version"> <xsl:attribute name="version"> <xsl:value-of select="@version"/> </xsl:attribute> </xsl:if> <xsl:if test="@revision"> <xsl:attribute name="revision"> <xsl:value-of select="@revision"/> </xsl:attribute> </xsl:if> <xsl:apply-templates select="@*"/> <xsl:comment> *************************************************************************** * * <xsl:value-of select="$product-version"/> * * THIS FILE IS AUTOMATICALLY GENERATED AND SHOULD NOT * BE MANUALLY EDITED. * * Generated using adl2canonical.xslt <xsl:value-of select="substring('$Revision: 1.10 $', 12)"/> * *************************************************************************** </xsl:comment> <xsl:apply-templates select="*"/> </xsl:copy> </xsl:template> <!-- an entity which already has a key tag - just copy it through --> <xsl:template match="adl:entity[adl:key]"> <xsl:comment> entity <xsl:value-of select="@name"/> already has a key - not generating one </xsl:comment> <entity> <xsl:if test="not(@magnitude)"> <xsl:attribute name="magnitude">6</xsl:attribute> </xsl:if> <xsl:if test="not(@volatility)"> <xsl:attribute name="volatility">0</xsl:attribute> </xsl:if> <xsl:if test="not( @table)"> <xsl:attribute name="table"> <xsl:value-of select="concat( $tablename-prefix, @name)"/> </xsl:attribute> </xsl:if> <xsl:apply-templates select="@* | node()"/> </entity> </xsl:template> <!-- an entity which has a '@natural-key' attribute. Since we've got the key tag, I think this should be disallowed --> <xsl:template match="adl:entity[@natural-key]"> <xsl:message terminate="no"> ADL WARNING: [In entity '<xsl:value-of select="@name"/>']: '@natural-key' is deprecated - use the 'key' sub element instead </xsl:message> <entity> <xsl:if test="not(@magnitude)"> <xsl:attribute name="magnitude">6</xsl:attribute> </xsl:if> <xsl:if test="not( @table)"> <xsl:attribute name="table"> <xsl:value-of select="concat( $tablename-prefix, @name)"/> </xsl:attribute> </xsl:if> <xsl:variable name="nkey" select="@natural-key"/> <xsl:apply-templates select="@*"/> <!-- children copied through in legal order, to ensure the document remains valid --> <xsl:apply-templates select="adl:documentation"/> <xsl:apply-templates select="adl:content"/> <key> <xsl:apply-templates select="adl:property[@name=$nkey]"/> <xsl:apply-templates select="adl:key/adl:property"/> </key> <xsl:apply-templates select="adl:prompt | adl:property[not(@name=$nkey)] | adl:one-to-many | adl:many-to-many | adl:many-to-one"/> <xsl:variable name="entity" select="."/> <xsl:for-each select="//adl:group"> <xsl:call-template name="entity-group-permission"> <xsl:with-param name="entity" select="$entity"/> <xsl:with-param name="group" select="."/> <xsl:with-param name="descendantname" select="@name"/> </xsl:call-template> </xsl:for-each> <xsl:apply-templates select="adl:form | adl:page | adl:list"/> </entity> </xsl:template> <!-- an entity which has no explicit key: auto-generate one --> <xsl:template match="adl:entity"> <xsl:comment> entity <xsl:value-of select="@name"/> has no key - generating one </xsl:comment> <entity> <xsl:if test="not(@magnitude)"> <xsl:attribute name="magnitude">6</xsl:attribute> </xsl:if> <xsl:if test="not( @table)"> <xsl:attribute name="table"> <xsl:value-of select="concat( $tablename-prefix, @name)"/> </xsl:attribute> </xsl:if> <!-- copy attributes through --> <xsl:apply-templates select="@*"/> <!-- children copied through in legal order, to ensure the document remains valid --> <xsl:apply-templates select="adl:documentation"/> <xsl:apply-templates select="adl:content"/> <key> <!-- autogenerate a key element... --> <!-- select a name... --> <xsl:variable name="key"> <xsl:choose> <xsl:when test="$abstract-key-name-convention='Name'"> <xsl:value-of select="@name"/> </xsl:when> <xsl:when test="$abstract-key-name-convention = 'NameId'"> <xsl:value-of select="concat( @name, 'Id')"/> </xsl:when> <xsl:when test="$abstract-key-name-convention = 'Name_Id'"> <xsl:value-of select="concat( @name, '_Id')"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="'Id'"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <!-- check that it is unique, and abort hard if not... --> <xsl:if test="descendant::adl:property[@name=$key]"> <xsl:message terminate="yes"> ADL ERROR: Entity '<xsl:value-of select="@name"/>' has a property '<xsl:value-of select="$key"/>' which conflicts with your chosen key naming convention <xsl:value-of select="$abstract-key-name-convention"/>. Either: (i) Make property '<xsl:value-of select="$key"/>' an explicit key by putting it in the <key> tag; (ii) Name property '<xsl:value-of select="$key"/>' something else; or (iii) Choose a different key naming convention. </xsl:message> </xsl:if> <!-- generate one property, the abstract primary key --> <property type="integer" distinct="system" required="true"> <xsl:attribute name="name"> <xsl:value-of select="normalize-space( $key)"/> </xsl:attribute> <generator action="native"/> <documentation> Auto-generated abstract primary key </documentation> </property> </key> <xsl:apply-templates select="adl:prompt | adl:property | adl:one-to-many | adl:many-to-many | adl:many-to-one"/> <xsl:variable name="entity" select="."/> <xsl:for-each select="//adl:group"> <xsl:call-template name="entity-group-permission"> <xsl:with-param name="entity" select="$entity"/> <xsl:with-param name="group" select="."/> <xsl:with-param name="descendantname" select="@name"/> </xsl:call-template> </xsl:for-each> <xsl:apply-templates select="adl:form | adl:page | adl:list"/> </entity> </xsl:template> <xsl:template match="@table"> <xsl:attribute name="table"> <xsl:value-of select="concat( $tablename-prefix, .)"/> </xsl:attribute> </xsl:template> <!-- If properties='all', unroll them into a properties='listed' form. We need to do this for lists, pages and forms; there's probably some clever way of doing it all in a oner, but I don't know what that is --> <xsl:template match="adl:form[ @properties='all']"> <form properties='listed'> <!-- copy the name attribute through --> <xsl:apply-templates select="@name"/> <!-- children copied through in legal order, to ensure the document remains valid --> <xsl:apply-templates select="adl:documentation"/> <!-- unroll the properties --> <xsl:call-template name="unroll-properties"/> <xsl:apply-templates select="adl:head|adl:top|adl:foot|adl:field| adl:fieldgroup|adl:auxlist|adl:verb| adl:permission|adl:pragma"/> </form> </xsl:template> <!-- If properties='all', unroll them into a properties='listed' form. We need to do this for lists, pages and forms; there's probably some clever way of doing it all in a oner, but I don't know what that is --> <xsl:template match="adl:page[ @properties='all']"> <page properties='listed'> <!-- copy the name attribute through --> <xsl:apply-templates select="@name"/> <!-- children copied through in legal order, to ensure the document remains valid --> <xsl:apply-templates select="adl:documentation"/> <!-- unroll the properties --> <xsl:call-template name="unroll-properties"/> <xsl:apply-templates select="adl:head|adl:top|adl:foot|adl:field| adl:fieldgroup|adl:auxlist|adl:verb| adl:permission|adl:pragma"/> </page> </xsl:template> <!-- If properties='all', unroll them into a properties='listed' form. We need to do this for lists, pages and forms; there's probably some clever way of doing it all in a oner, but I don't know what that is --> <xsl:template match="adl:list[ @properties='all']"> <list properties='listed'> <!-- copy the name attribute through --> <xsl:apply-templates select="@name"/> <!-- children copied through in legal order, to ensure the document remains valid --> <xsl:apply-templates select="adl:documentation"/> <!-- unroll the properties --> <xsl:call-template name="unroll-properties-no-lists"/> <xsl:apply-templates select="adl:head|adl:top|adl:foot|adl:field| adl:fieldgroup|adl:auxlist|adl:verb| adl:permission|adl:pragma"/> </list> </xsl:template> <!-- In practice it's likely only to be lists which have properties='user-distinct', but the grammar allows this for pages and forms as well so we'll deal with it --> <xsl:template match="adl:form[ @properties='user-distinct']"> <form properties='listed'> <!-- copy the name attribute through --> <xsl:apply-templates select="@name"/> <!-- children copied through in legal order, to ensure the document remains valid --> <xsl:apply-templates select="adl:documentation"/> <!-- unroll the properties --> <xsl:call-template name="unroll-user-distinct"/> <xsl:apply-templates select="adl:head|adl:top|adl:foot|adl:field| adl:fieldgroup|adl:auxlist|adl:verb| adl:permission|adl:pragma"/> </form> </xsl:template> <!-- In practice it's likely only to be lists which have properties='user-distinct', but the grammar allows this for pages and forms as well so we'll deal with it --> <xsl:template match="adl:page[ @properties='user-distinct']"> <page properties='listed'> <!-- copy the name attribute through --> <xsl:apply-templates select="@name"/> <!-- children copied through in legal order, to ensure the document remains valid --> <xsl:apply-templates select="adl:documentation"/> <!-- unroll the properties --> <xsl:call-template name="unroll-user-distinct"/> <xsl:apply-templates select="adl:head|adl:top|adl:foot|adl:field| adl:fieldgroup|adl:auxlist|adl:verb| adl:permission|adl:pragma"/> </page> </xsl:template> <!-- In practice it's likely only to be lists which have properties='user-distinct', but the grammar allows this for pages and forms as well so we'll deal with it --> <xsl:template match="adl:list[ @properties='user-distinct']"> <list properties='listed'> <!-- copy the name attribute through --> <xsl:apply-templates select="@name"/> <!-- children copied through in legal order, to ensure the document remains valid --> <xsl:apply-templates select="adl:documentation"/> <!-- unroll the properties --> <xsl:call-template name="unroll-user-distinct"/> <xsl:apply-templates select="adl:head|adl:top|adl:foot|adl:field| adl:fieldgroup|adl:auxlist|adl:verb| adl:permission|adl:pragma"/> </list> </xsl:template> <xsl:template match="adl:typedef[ @type = 'string' and not( @size)]"> <xsl:message terminate="yes"> ADL ERROR: Type definitions of type 'string' must have a valid value for 'size' Offending typedef: <xsl:value-of select="@name"/> </xsl:message> </xsl:template> <!-- Language constraints --> <xsl:template match="adl:property[ @type = 'string' and not( @size)]"> <xsl:message terminate="yes"> ADL ERROR: Properties of type 'string' must have a valid value for 'size' Offending property: <xsl:value-of select="@name"/> of entity <xsl:value-of select="ancestor::adl:entity/@name"/> </xsl:message> </xsl:template> <xsl:template match="adl:property[ @type = 'entity' and not( @entity)]"> <xsl:message terminate="yes"> ADL ERROR: Properties of type 'entity' must have a valid value for 'entity' Offending property: <xsl:value-of select="@name"/> of entity <xsl:value-of select="ancestor::adl:entity/@name"/> </xsl:message> </xsl:template> <xsl:template match="adl:property[ @type = 'defined' and not( @typedef)]"> <xsl:message terminate="yes"> ADL ERROR: Properties of type 'defined' must have a valid value for 'typedef' Offending property: <xsl:value-of select="@name"/> of entity <xsl:value-of select="ancestor::adl:entity/@name"/> </xsl:message> </xsl:template> <!-- copy anything that isn't explicitly matched --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- generate a permission element for this group and this entity --> <xsl:template name="entity-group-permission"> <xsl:param name="entity"/> <xsl:param name="group"/> <xsl:param name="descendantname"/> <xsl:choose> <xsl:when test="$entity/adl:permission[@group=$group/@name]"> <!-- there's an explicit permission for this group --> <permission> <xsl:attribute name="group"> <xsl:value-of select="$descendantname"/> </xsl:attribute> <xsl:attribute name="permission"> <xsl:value-of select="$entity/adl:permission[@group=$group/@name]/@permission"/> </xsl:attribute> </permission> </xsl:when> <xsl:when test="$group/@parent"> <xsl:choose> <xsl:when test="$group/@parent = $group/@name"> <xsl:message terminate="yes"> ADL: ERROR: A group may not be its own parent; offending group <xsl:value-of select="$group/@name"/> </xsl:message> </xsl:when> <xsl:when test="//adl:group[@name=$group/@parent]"> <xsl:call-template name="entity-group-permission"> <xsl:with-param name="entity" select="$entity"/> <xsl:with-param name="group" select="//adl:group[@name=$group/@parent]"/> <xsl:with-param name="descendantname" select="$descendantname"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:message terminate="yes"> ADL: ERROR: Group specified (<xsl:value-of select="$group/@parent"/> does not exist. </xsl:message> </xsl:otherwise> </xsl:choose> </xsl:when> <xsl:otherwise> <permission permission="none"> <xsl:attribute name="group"> <xsl:value-of select="$descendantname"/> </xsl:attribute> </permission> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- unroll all the explicit properties in the ancestor entity of the context (assumed to be form, page or list) into a list of fields --> <xsl:template name="unroll-properties"> <xsl:for-each select="ancestor::adl:entity/descendant::adl:property | ancestor::adl:entity/descendant::adl:one-to-many | ancestor::adl:entity/descendant::adl:many-to-many | ancestor::adl:entity/descendant::adl:many-to-one"> <field> <xsl:attribute name="property"> <xsl:value-of select="@name"/> </xsl:attribute> <xsl:copy-of select="*"/> </field> </xsl:for-each> </xsl:template> <!-- unroll all the explicit properties in the ancestor entity of the context (assumed to be form, page or list) into a list of fields, ommitting those properties which are inherently lists --> <xsl:template name="unroll-properties-no-lists"> <xsl:for-each select="ancestor::adl:entity/descendant::adl:property"> <xsl:choose> <xsl:when test="@type='list'"/> <xsl:when test="@type='link'"/> <xsl:otherwise> <field> <xsl:attribute name="property"> <xsl:value-of select="@name"/> </xsl:attribute> <xsl:copy-of select="*"/> </field> </xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:template> <!-- unroll all the user-distinct properties in the ancestor entity of the context (assumed to be form, page or list) into a list of fields. NOTE that n-to-n properties cannot currently be user-distinct and are therefore not inspected --> <xsl:template name="unroll-user-distinct"> <xsl:for-each select="ancestor::adl:entity/descendant::adl:property[@distinct='user' or @distinct='all']"> <field> <xsl:attribute name="property"> <xsl:value-of select="@name"/> </xsl:attribute> <xsl:apply-templates select="adl:prompt"/> <xsl:apply-templates select="adl:help"/> <xsl:copy-of select="*"/> </field> </xsl:for-each> </xsl:template> </xsl:stylesheet>