<?xml version="1.0"?>

<!-- APEX (Architectural Processor Employing XSLT), an XSLT stylesheet
for creating an architectural document from a client document.

     Author: Joshua Lubell
             lubell@nist.gov

     $Id: apex.xsl,v 1.9 2000/07/07 18:29:27 lubell Exp $

THIS STYLESHEET HAS NOT BEEN APPROVED BY NIST FOR PUBLIC DISTRIBUTION!

APEX implements a simple subset of the Architectural Form Definition
Requirements (AFDR) specified in Annex A.3 of ISO 10744:1997. APEX
behaves similarly to David Megginson's XAF package for Java
(http://www.megginson.com/XAF/) and differs from the AFDR in the same
ways as XAF. In addition, APEX differs from the AFDR as follows:

1. Bridge forms are not supported.

2. The architecture use declaration processing instruction is
ingored. Information about an architecture is instead conveyed to APEX
through stylesheet parameters.

The APEX stylesheet requires the following parameter:

name - the name of the architecture being processed

The following optional parameters correspond to pseudo-attributes from 
the architecture use declaration:

form-att
auto
renamer-att
suppressor-att
ignore-data-att

The following parameter is also optional:

strip - a list of attributes to be stripped during architectural
processing, specified as a space-delimited string. This is useful for
suppressing architectural support attributes from architectures other
than the architecture being processed. For example, if your XML
document is a client of architectures "foo" and "bar", you want to
create an architectural document for "foo", and your document contains
"bar" form attributes and "bar-atts" renamer attributes for the bar
architecture, you would specify the value "bar bar-atts" for the strip
parameter.

To use APEX, simply process your client document using the apex.xsl
stylesheet and your favorite XSLT processor. Supply a value for the
required "name" parameter as well as any desired optional parameters.

-->

<xsl:stylesheet version="1.0"
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <!-- Global parameters -->

  <!-- name of the architecture being processed -->
  <xsl:param name="name"/>

  <!-- values from the architecture use declaration -->
  <xsl:param name="form-att" select="''"/>
  <xsl:param name="renamer-att" select="''"/>
  <xsl:param name="suppressor-att" select="''"/>
  <xsl:param name="ignore-data-att" select="''"/>
  <xsl:param name="auto" select="''"/>

  <!-- space-delimited list of support attributes from architectures
other than the architecture being processed -->
  <xsl:param name="strip" select="''"/>

  <!-- Global variables -->

  <!-- form-att-name
       set to value of form-att if specified in architecture use
declaration; otherwise set to architecture name -->
  <xsl:variable name="form-att-name">
    <xsl:choose>
      <xsl:when test="$form-att=''">
        <xsl:value-of select="$name"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$form-att"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <!-- auto-map
       set to value of auto if specified in architecture use
declaration; otherwise set to ArcAuto -->
  <xsl:variable name="auto-map">
    <xsl:choose>
      <xsl:when test="$auto=''">
        <xsl:value-of select="'ArcAuto'"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$auto"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <!-- Template rules -->

  <!-- Template rule parameters:

       suppress-state - specifies whether to suppress architectural
processing; value determined from suppressor architecture use
declaration attribute; default is 'sArcNone'

       ignore-state - specifies whether to ignore data; value
determined from ignore data architecture use declaration attribute;
default is 'cArcIgnD' -->

  <xsl:template match="/">
    <xsl:apply-templates>
      <xsl:with-param name="suppress-state" select="'sArcNone'"/>
      <xsl:with-param name="ignore-state" select="'cArcIgnD'"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="*">
    <!-- Unless architectural processing is irrevocably suppressed,
update suppress-state and ignore-state, process the element arcording
to the old suppress-state, and process the element's content using the 
updated suppress-state and ignore-state. -->
    <xsl:param name="suppress-state"/>
    <xsl:param name="ignore-state"/>
    <xsl:choose>
      <xsl:when test="$suppress-state='sArcNone'">
        <!-- Update suppress-state if a suppressor attribute value is
specified and ignore-state if an ignore data attribute is
specified. Process the element. Process the element's content
according to the updated suppress-state and ignore-state. -->
        <xsl:call-template name="process-element">
          <xsl:with-param name="suppress-state">
            <xsl:call-template name="set-suppress-state">
              <xsl:with-param name="suppress-state"
			      select="$suppress-state"/>
            </xsl:call-template>
          </xsl:with-param>
          <xsl:with-param name="ignore-state">
            <xsl:call-template name="set-ignore-state">
              <xsl:with-param name="ignore-state"
			      select="$ignore-state"/>
            </xsl:call-template>
          </xsl:with-param>
        </xsl:call-template>
      </xsl:when>
      <xsl:when test="$suppress-state='sArcForm'">
        <!-- Update suppress-state if a suppressor attribute value is
specified and ignore-state if an ignore data attribute is
specified. Don't process the element. Process the element's content
using the updated suppress-state and ignore-state. -->
        <xsl:call-template name="apply-data-only">
          <xsl:with-param name="suppress-state">
            <xsl:call-template name="set-suppress-state">
              <xsl:with-param name="suppress-state"
			      select="$suppress-state"/>
            </xsl:call-template>
          </xsl:with-param>
          <xsl:with-param name="ignore-state">
            <xsl:call-template name="set-ignore-state">
              <xsl:with-param name="ignore-state"
			      select="$ignore-state"/>
            </xsl:call-template>
          </xsl:with-param>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
         <!-- suppress-state is sArcAll, so don't do any processing at 
all. -->
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="text()">
    <!-- Copy the data if architectural processing is not being
suppressed and the ignore-state is nArcIgnD. If architectural
processing is not being suppressed and the ignore-state is cArcIgnD,
copy the data only the parent element is architectural. The parent
element is architectural if it has a form attribute with a value other 
than '#IMPLIED', or if element-type names are being derived
automatically and there is no form attribute with the value
'#IMPLIED'. -->
    <xsl:param name="suppress-state"/>
    <xsl:param name="ignore-state"/>
    <xsl:if test="$suppress-state='sArcNone'">
      <xsl:choose>
        <xsl:when test="$ignore-state='nArcIgnD'">
          <xsl:value-of select="."/>
        </xsl:when>
        <xsl:when test="$ignore-state='cArcIgnD'">
          <xsl:choose>
            <xsl:when test="../@*[name()=$form-att-name] and not(../@*[name()=$form-att-name]='#IMPLIED')">
              <xsl:value-of select="."/>
            </xsl:when>
            <xsl:when test="$auto-map='ArcAuto' and
	            not(../@*[name()=$form-att-name])">
              <xsl:value-of select="."/>
            </xsl:when>
          </xsl:choose>
        </xsl:when>
      </xsl:choose>
    </xsl:if>
  </xsl:template>

  <xsl:template match="@*">
    <!-- Attributes are architectural unless they are mapped to
'#DEFAULT' by an attribute renamer. Therefore, if the attribute has no 
renamer sibling, copy it. If a renamer sibling exists, inspect the
renamer attribute's value. If the attribute being processed does not
appear as a client attribute name in the list of pairs assigned to the
renamer attribute, copy the attribute unmodified. If the attribute
being processed appears maps to an architectural attribute, substitute
the architectural attribute name for the client attribute name. If the
attribute being processed maps to '#DEFAULT', don't copy the
attribute. -->
    <xsl:param name="suppress-state"/>
    <xsl:param name="ignore-state"/>
    <xsl:choose>
      <xsl:when test="../@*[name()=$renamer-att]">
        <xsl:variable name="newname">
          <xsl:call-template name="rename">
            <xsl:with-param name="attr" select="name()"/>
            <xsl:with-param name="list" select="../@*[name()=$renamer-att]"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:choose>
          <xsl:when test="$newname=''">
            <xsl:call-template name="copy-att"/>
          </xsl:when>
          <xsl:when test="$newname='#DEFAULT'"/>
          <xsl:otherwise>
            <xsl:attribute name="{$newname}">
              <xsl:value-of select="string(.)"/>
            </xsl:attribute>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="copy-att"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <!-- Named templates -->

  <xsl:template name="copy-att">
    <!-- Copy the attribute currently being processed unless it is an
architecture use declaration attribute or is specified in the 'strip'
stylesheet parameter. -->
    <xsl:variable name="is-arch-use-attr">
      <xsl:call-template name="member">
        <xsl:with-param name="item" select="name()"/>
        <xsl:with-param name="list" select="$strip"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:if test=
"not(name()= $is-arch-use-attr or name()=$form-att-name or name()=$renamer-att or name()=$suppressor-att or name()=$ignore-data-att)">
      <xsl:copy/>
    </xsl:if>
  </xsl:template>

  <xsl:template name="apply-all">
    <!-- Process an element's attributes and content. -->
    <xsl:param name="suppress-state"/>
    <xsl:param name="ignore-state"/>
    <xsl:apply-templates select="*|@*|text()">
      <xsl:with-param name="suppress-state" select="$suppress-state"/>
      <xsl:with-param name="ignore-state" select="$ignore-state"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template name="apply-data-only">
    <!-- Process an element's content. -->
    <xsl:param name="suppress-state"/>
    <xsl:param name="ignore-state"/>
    <xsl:apply-templates select="*|text()">
      <xsl:with-param name="suppress-state" select="$suppress-state"/>
      <xsl:with-param name="ignore-state" select="$ignore-state"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template name="deep-copy">
    <!-- Copy an element, and process its attributes and content. -->
    <xsl:param name="suppress-state"/>
    <xsl:param name="ignore-state"/>
    <xsl:copy>
      <xsl:call-template name="apply-all">
        <xsl:with-param name="suppress-state" select="$suppress-state"/>
        <xsl:with-param name="ignore-state" select="$ignore-state"/>
      </xsl:call-template>
    </xsl:copy>
  </xsl:template>

  <xsl:template name="deep-copy-data-only">
    <!-- Copy an element, and process its content. -->
    <xsl:param name="suppress-state"/>
    <xsl:param name="ignore-state"/>
    <xsl:copy>
      <xsl:call-template name="apply-data-only">
        <xsl:with-param name="suppress-state" select="$suppress-state"/>
        <xsl:with-param name="ignore-state" select="$ignore-state"/>
      </xsl:call-template>
    </xsl:copy>
  </xsl:template>

  <xsl:template name="process-element">
    <!-- Perform architectural processing on the current
element. Process its content as per the suppress-state and
ignore-state. -->
    <xsl:param name="suppress-state"/>
    <xsl:param name="ignore-state"/>
    <xsl:choose>
      <xsl:when test="$auto-map='ArcAuto' and not(@*[name()=$form-att-name])">
        <!-- Element names are derived automatically, and no form
attribute exists. Copy the element and process its attributes and
content. -->
        <xsl:call-template name="deep-copy">
          <xsl:with-param name="suppress-state"
			  select="$suppress-state"/>
          <xsl:with-param name="ignore-state" select="$ignore-state"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:when test=
"@*[name()=$form-att-name]='#IMPLIED' or ($auto-map='nArcAuto' and not(@*[name()=$form-att-name]))">
        <!-- A form attribute maps this element to '#IMPLIED', or
automatic element-type name derivation is turned off and no mapping
is specified for this element. Don't include this element in the
architectural document, but process its content. -->
        <xsl:call-template name="apply-data-only">
          <xsl:with-param name="suppress-state"
			  select="$suppress-state"/>
          <xsl:with-param name="ignore-state" select="$ignore-state"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:when test="@*[name()=$form-att-name]">
        <!-- A form attribute maps this element to an architectural
element, so substitute the architectural name for the client
name. Process the element's attributes and content. -->
        <xsl:element name="{@*[name()=$form-att-name]}">
          <xsl:call-template name="apply-all">
            <xsl:with-param name="suppress-state"
	  		  select="$suppress-state"/>
            <xsl:with-param name="ignore-state" select="$ignore-state"/>
          </xsl:call-template>
        </xsl:element>
      </xsl:when>
    </xsl:choose>
  </xsl:template>

  <xsl:template name="set-suppress-state">
    <!-- If the element currently being processed has a suppressor
attribute whose value is not '#IMPLIED', set suppress-state to this
value. -->
    <xsl:param name="suppress-state"/>
    <xsl:choose>
      <xsl:when test=
"@*[name()=$suppressor-att] and not(@*[name()=$suppressor-att]='#IMPLIED')">
        <xsl:value-of select="@*[name()=$suppressor-att]"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$suppress-state"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template name="set-ignore-state">
    <!-- If the element currently being processed has an ignore data
attribute whose value is not '#IMPLIED', set ignore-state to this
value. -->
    <xsl:param name="ignore-state"/>
    <xsl:choose>
      <xsl:when test=
"@*[name()=$ignore-data-att] and not(@*[name()=$ignore-data-att]='#IMPLIED')">
        <xsl:value-of select="@*[name()=$ignore-data-att]"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$ignore-state"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template name="rename">
    <!-- Given the name of an attribute from the client document and a
list of pairs associating client attribute names with architectural
attribute names, return the architectural attribute name associated
with the client attribute. If the client attribute name does not
appear in the list of pairs, return ''. -->
    <xsl:param name="attr"/>
    <xsl:param name="list"/>
    <xsl:variable name="normlist">
      <xsl:value-of select="normalize-space($list)"/>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="$normlist=''">
        <xsl:value-of select="''"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:variable name="normlist-1">
          <xsl:value-of select="substring-after($normlist,' ')"/>
        </xsl:variable>
        <xsl:variable name="clientattr">
          <xsl:value-of select="substring-before($normlist,' ')"/>
        </xsl:variable>
        <xsl:variable name="archattr">
          <xsl:variable name="temp"
			select="substring-before($normlist-1,' ')"/>
            <xsl:choose>
              <xsl:when test="$temp=''">
                <xsl:value-of select="$normlist-1"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:value-of select="$temp"/>
              </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>
        <xsl:choose>
          <xsl:when test="$clientattr=$attr">
            <xsl:value-of select="$archattr"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:call-template name="rename">
              <xsl:with-param name="attr" select="$attr"/>
              <xsl:with-param name="list"
			      select="substring-after($normlist-1,' ')"/>
            </xsl:call-template>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template name="member">
    <!-- Determine whether 'item', a string, is a member of 'list', a
string whose value is a space-delimited list of substrings. Return
item if it is a member; an empty string if it is not. -->
    <xsl:param name="item"/>
    <xsl:param name="list"/>
    <xsl:choose>
      <xsl:when test="$list=''">
        <xsl:value-of select="''"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:variable name="normlist">
          <xsl:value-of select="normalize-space($list)"/>
        </xsl:variable>
        <xsl:variable name="rest">
          <xsl:value-of select="substring-after($normlist,' ')"/>
        </xsl:variable>
        <xsl:variable name="first">
          <xsl:choose>
            <xsl:when test="contains($normlist,' ')">
              <xsl:value-of select="substring-before($normlist,' ')"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="$normlist"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:variable>
        <xsl:choose>
          <xsl:when test="$first=$item">
            <xsl:value-of select="$item"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:call-template name="member">
              <xsl:with-param name="item" select="$item"/>
              <xsl:with-param name="list" select="$rest"/>
            </xsl:call-template>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

