Overview

This article uses the XML dataset defined in the requirements detailed report. Users are directed to run this "requirements detailed" report in XML format to see the data available. In this article, we will create a new XSLT has been created which iterates through the requirements identifying the following fields.

  • Hierarchy (Will be created dynamically at the rendering time based on the current hierarchical requirement structure)
  • Requirement ID (Existing Requirement ID)
  • Name (Existing Requirement Name)
  • Parent Requirement (The immediate parent of the current requirement)
  • Child Requirements (A comma separated list of the immediate children of the current requirement)
  • Associated Requirements (A comma separated list of requirements laterally associated with the current requirement) 
  • Test Cases (A comma separated list of ordered test cases)

Modify Standard Section

  1. Clone the Requirements Detailed Report
  2. Update the name and description of the new report (shown below)
  3. Click on the "Customize" button near the "Requirements Details" in the "Standard Sections".
    Modify Standard Section
  4. Update the XSLT with the one provided below
    Report Config
  5. Save the "Standard Section" (The Save button is referenced in the screenshot above)
  6. Save the report (There will be another Save button to save the report)
    Saving the Report
  7. You are now ready to run the report from the Reporting area.

Modified XSLT

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html" indent="yes"/>

  <!-- Root template -->
  <xsl:template match="/RequirementData">
    <html>
      <head>
        <title>Requirements Traceability</title>
      </head>
        <style>
          body { font-family: Arial; font-size: 12px; }
          table.DataGrid { border-collapse: collapse; width: 100%; }
          .DataGrid th, .DataGrid td {
            border: 1px solid #ccc; padding: 6px;
            vertical-align: top; word-break: break-word;
          }
          .DataGrid th { background: #f2f2f2; text-align: left; }
          tr:nth-child(even) td { background: #fafafa; }
          .footnote { margin-top: 10px; font-style: italic; color: #666; }
        </style>
      <body>
        <h2>Requirements Traceability Report</h2>
        <table class="DataGrid">
          <tr>
            <th>Hierarchy</th>
            <th>Requirement ID</th>
            <th>Requirement Name</th>
            <th>Parent Requirement ID</th>
            <th>Requirement Reference</th>
            <th>Child Requirements</th>
            <th>Test Case IDs</th>
          </tr>

          <!-- Start from top-level requirements -->
          <xsl:apply-templates select="Requirement[string-length(IndentLevel)=3]" mode="render">
            <xsl:with-param name="prefix" select="''"/>
          </xsl:apply-templates>

        </table>
      </body>
    </html>
  </xsl:template>


  <!-- ================================================= -->
  <!-- Recursive template to render requirement and children -->
  <!-- ================================================= -->
  <xsl:template match="Requirement" mode="render">
    <xsl:param name="prefix"/>

    <xsl:variable name="reqId" select="RequirementId"/>
    <xsl:variable name="reqName" select="Name"/>
    <xsl:variable name="indentLevel" select="(string-length(IndentLevel) div 3) + 1"/>

    <!-- Sequential number among siblings (XSLT 1.0 safe) -->
    <xsl:variable name="seq">
      <xsl:value-of
        select="count(
                  preceding-sibling::Requirement
                    [string-length(IndentLevel)=string-length(current()/IndentLevel)]
                ) + 1"/>
    </xsl:variable>

    <!-- Hierarchical number -->
    <xsl:variable name="hierNum">
      <xsl:choose>
        <xsl:when test="string-length($prefix)=0">
          <xsl:value-of select="$seq"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="concat($prefix,'.',$seq)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <!-- Parent ID: indent minus 3 chars -->
    <xsl:variable name="parentIndent"
                  select="substring(IndentLevel,1,string-length(IndentLevel)-3)"/>
    <xsl:variable name="parentId"
                  select="/RequirementData/Requirement[IndentLevel=$parentIndent]/RequirementId"/>

    <!-- Output current requirement row -->
    <tr>
      <td><xsl:value-of select="$hierNum"/></td>
      <td><xsl:value-of select="$reqId"/></td>
      <td>
        <xsl:call-template name="indent">
          <xsl:with-param name="level" select="$indentLevel"/>
        </xsl:call-template>
        <xsl:value-of select="$reqName"/>
      </td>
      <td><xsl:value-of select="$parentId"/></td>

              <!-- =====================================
                   Requirement Associations(Lateral Links of type=1)
              ====================================== -->
              <td>
                <xsl:for-each select="Requirements/ArtifactLink[ArtifactTypeId=1]">
                  <xsl:value-of select="ArtifactId"/>
                  <xsl:if test="position()!=last()">, </xsl:if>
                </xsl:for-each>
              </td>

              <!-- =====================================
                   Immeidate Child Requirements (one level down)
              ====================================== -->
              <td>
			    <!-- Calculate depth from IndentLevel -->
				<xsl:variable name="indent" select="IndentLevel"/>
				<xsl:variable name="level" select="string-length($indent) div 3"/>
                <xsl:variable name="nextLevel" select="string-length($indent) + 3"/>
                <xsl:for-each select="following-sibling::Requirement[
                      string-length(IndentLevel) = $nextLevel
                      and generate-id(
                         preceding-sibling::Requirement[
                           string-length(IndentLevel) &lt;= string-length($indent)
                         ][1]
                      ) = generate-id(current())
                ]">
                  <xsl:value-of select="RequirementId"/>
                  <xsl:if test="position() != last()">, </xsl:if>
                </xsl:for-each>
              </td>

	  
      <td>
        <xsl:for-each select="TestCases/TestCase">
          <xsl:value-of select="TestCaseId"/>
          <xsl:if test="position()!=last()">, </xsl:if>
        </xsl:for-each>
      </td>
    </tr>

    <!-- Process child requirements recursively -->
    <xsl:variable name="childPrefix" select="$hierNum"/>
    <xsl:apply-templates
      select="/RequirementData/Requirement
              [substring(IndentLevel,1,string-length(IndentLevel)-3)=current()/IndentLevel]"
      mode="render">
      <xsl:with-param name="prefix" select="$childPrefix"/>
    </xsl:apply-templates>

  </xsl:template>

  <!-- Indentation helper -->
  <xsl:template name="indent">
    <xsl:param name="level"/>
    <xsl:if test="$level &gt; 1">
      <xsl:text>&#160;&#160;&#160;</xsl:text>
      <xsl:call-template name="indent">
        <xsl:with-param name="level" select="$level - 1"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

 

Summary Explanation 

  • Indent Level-based hierarchy: Each requirement’s depth is determined by the length of its IndentLevel string — every 3 characters (e.g., AAA, AAAAAA, AAAAAAAAA) represents one level in the hierarchy.

  • Sibling numbering: For each requirement, the XSLT counts how many preceding siblings exist at the same level to assign its position number (e.g., first child = .1, second child = .2, etc.).

  • Parent–child relationship: The parent requirement ID is found by locating the nearest previous requirement with a shorter IndentLevel (one level higher), ensuring proper parent linkage.

  • Hierarchical label construction: The full hierarchy number (e.g., 3.1.4.2) is built recursively by combining the parent’s hierarchy number with the current requirement’s sibling count — creating the ordered structure seen in the report.

  • Child requirements: The child requirements are found by locating the current requirements indent level + 3 (That is if AAAAAA is the current requirement with indent=6, we look up for indent=9 such as AAAAAAAAA, AAAAAAAAB, etc.).

  • Lateral requirements: These are the requirements that are associated in the "Associations" tab of the current requirement. Since many artifacts can be associated, we filter these requirement list by ArtifactType=1 to search for requirements. Note that there is no parent-child relationship here. These are lateral or horizontal requirements.

Sample Data Illustration

Given below is the sample data illustration of a requirement with its parent and immediate children.

Grand Parent, Parent, and Child Relationship

 

Lateral Association - Data Setup

Given below is an example of three requirements laterally associated.

Lateral Association

Verification - Test Cases Setup

Given below is the list of test cases currently tied to the specific requirement illustrated.

Test Cases Setup

Report Output

Given below is the report output. Highlighted using the same color schemes in the earlier data setup scenarios, the report outlines the parent requirement for a specific requirement, its lateral associations as well as the immediate level children.

Report Ouptput

 

Callouts

It is possible that the outline scheme may sometime skip numbers. The logic in this report is constrained by the XSLT reporting considerations and does not completely reproduce the outline logic in the 'document' view of the requirements. This could happen because of the following potential reasons.

  • Requirements that once existed but has been deleted subsequently.
  • The requirement may exist but has been filtered by some information (like owner, status, etc.)
  • Requirements may have imported through a third party tool with inconsistent hierarchy. 
  • Manual movement of requirements in the hierarchy by skipping some indent levels.  

Possible Improvements

1. In the list of test cases or even possibly the parent requirement id, we are bringing only the identifier. Sometimes, customers may prefer to have a prefix like  "TC" or "RQ" before the id. If that is the case, you may want to add the prefix in the area of the XSLT where the appropriate id is listed. For example, the following code enhancements will add a prefix "TC" before the test case id.

<td>
  TC<xsl:value-of select="TestCaseId"/>
</td>

2. If you would prefer to have the test case ids sorted in the comma separated list for ease, then, you can also implement a sort before the TestCaseIds are listed by applying this code.

 <xsl:sort select="TestCaseId"/>