RE: [xsl] Advice/feedback on stylesheet?

Subject: RE: [xsl] Advice/feedback on stylesheet?
From: "M. David Peterson" <m.david@xxxxxxxxxx>
Date: Sat, 27 Mar 2004 09:17:39 -0700
Well, It just so happens that I am currently in the documentation
process of a project I just finished and have been keeping my eye out
for projects on the list that can act as good tutorial material for the
"concepts of XSLT and functional programming" section of my docs.  I
don't know if you should be excited or frightened (I tend to get a
little "windy" sometimes in my docs, but for good purpose.. I want to
make sure that all the information is available to showcase and help
answer questions as to the process of how and why something works and
not just leave it up to the end user to figure out for him or her self.)
but I saw your posting this morning and decided to use what will soon be
the not so quiet hours of a Saturday morning here in the Rocky Mountains
to drink my coffee and create a tutorial that solutions your project
into an 100% functional based process flow that uses temporary tree
structures to add a layer less of flatness to your data which then
allows for the beauty of the naturally occurring recursive process
contained in XSL and other functional programming languages to showcase
it stuff.  Ive commented the entire stylesheet so I will just leave it
up to my comments to explain the process.  Please, if you or anybody has
comments or questions please let me know.  My desire is to create self
teaching tutorials that assume just enough knowledge of functional
programming to make the reader dangerous and yet not so technical that
even the processor has a hard time understand what I'm trying to say/do.
As such, I give you version 0.1 of "the flat-file recursive rebuild
project".  Enjoy!  

Oh, by the way... I would copy and paste the text of this into an editor
of some sort as I didn't use any line breaks in my comments.  Depending
on your page width and if you have word wrap on the following code could
look more like the decompiled code of a 16-bit Windows EXE than a
printy-printed XML-based document structure.  My purpose for using this
particular "comment right after the associated code element that goes on
forever" is that I have another template that takes the structure of the
xsl:stylesheet and creates a CSS/HTML tree structure that allows access
to each related comment by simply clicking on the node representation of
the xsl:element in the tree structure.  I have a site under development
(still under heavy wraps as it is a fairly extensive effort that I want
to showcase all at once and not in bits and pieces) that will allow
access to these tools and templates to allow for much easier reading and
therefore understanding of the content using this documentation
technique.  Stay in tune to the list for further details in the not so
distant future.

Best of luck!  This XSL uses your original XML data structure which I
left intact at the bottom of this email for others to play with if they
desire.

<M:D/>

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
xmlns:xalan="http://xml.apache.org/xalan"; version="1.0">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="/"><!-- NOTE: match root of XML Doc -->
    <xsl:element name="ROWSET"><!-- NOTE: Create ROWSET Element for
output -->
      <xsl:apply-templates select="//NODE[LEVEL_ = '1']"
mode="group"><!-- NOTE: apply-templates to just to only the NODE
elements have a value 0f '1' for the LEVEL_ attribute and use the mode
attribute set to group -->
        <xsl:sort select="LEVEL_"/><!-- NOTE: Sort by the LEVEL_ element
to ensure we get the results in the proper order.  OBVIOUSLY this
particular case doesnt require us to sort them given that we are only
matching to "LEVEL_" values of 1 in our XPath statement.  But, for the
sake of good practice (if the criteria every changes then the sorting is
already in place) I put it in there. -->
        <xsl:with-param name="allElements" select="*"/><!-- NOTE: pass
the parameter all element, seleceting all the elements in the XML Doc
-->
      </xsl:apply-templates>
    </xsl:element>
  </xsl:template>
  <xsl:template match="NODE" mode="group"><!-- NOTE: this template
matches all "NODE" elements that were called from apply-templates that
used the mode attribute of "group" -->
    <xsl:param name="allElements"/><!-- NOTE: create parameter that will
be set to the value passed by the template -->
    <xsl:variable name="SRCD" select="SRCD"/><!-- NOTE: create SCRD
variable for each NODE element that passes through this template.  This
will be used to compare to the first character of all the other NODE
elements for grouping -->
    <xsl:variable name="groups"><!-- NOTE: create a variable named group
that holds the Result Tree Fragment of the grouping process -->
      <xsl:element name="group"><!-- NOTE: create element "group" that
will the hold the contents of each distinct group that is passed from
the origination template -->
        <xsl:apply-templates select="$allElements/NODE[starts-with(SRCD,
$SRCD)]" mode="groupTree"><!-- NOTE: apply-templates using the
allElements parameter passed from the previous template.  This parameter
contains and RTF of all the elements and attributes in the XML Doc.  The
processor will look for the first element in the RTF created by the
XPath in the select attribute and match it to the first template that
contains the proper element in the match attribute and the mode
attribute set to "groupTree" -->
          <xsl:sort select="LEVEL_"/><!-- NOTE: sort using the "LEVEL_"
value element -->
          <xsl:sort select="translate(SRCD, ',', '')"/><!-- NOTE:  then
sort on the translated value of the SRCD element (this removes the
commas so the processor can sort them as standard integers.  This isnt
always necessary but its good practice to ensure accurate results EVERY
time -->
        </xsl:apply-templates>
      </xsl:element>
    </xsl:variable>
    <xsl:apply-templates select="xalan:nodeset($groups)"
mode="groupTreeBranches"/><!-- NOTE:  use the nodeset function of your
particular processor (dont forget to declare the namespace extension in
your xsl:stylesheet decleration to convert the RTF created by the above
apply-templates process -->
  </xsl:template>
  <xsl:template match="NODE" mode="groupTree"><!-- NOTE:  the above
template matches each NODE elements that matched the grouping criteria
created in the above templates.  In this case it took each NODE that had
a "LEVEL_" element value of '1'.  The next template matched the first
character in the "SRCD" element of every node with the 4 elements that
matched the previous templates rules.  So in this case the first
template that matched -->
    <xsl:element name="NODE"><!-- NOTE:  It the creates a NODE element
in the output stream -->
      <xsl:copy-of select="child::*"/><!-- NOTE:  and then copys all the
elements contained within the originating NODE in the output stream as
well -->
    </xsl:element><!-- once this process has completed (all 4 original
matching elements are grouped properly with there respective elements)
the second apply-templates will be called, parsing through the RTF
created by this process matching (to the specified XPath) each element
contained within the direct document flow (meaning the sibling elements
that were matched with the XPath statement).  To access any elements
contained within these sibling elements will require another
apply-templates call from the next template (if recursive behavior is
required.  Given that the XML Doc file started as a flat file doesnt
allow for a simple example of the recursive nature of apply-templates
but the process has at least given us four distinct groups to work with
now which will allow some recursion processing to make our job easier.
-->
  </xsl:template>
  <xsl:template match="group" mode="groupTreeBranches"><!-- NOTE:  the
template before this one created a nodeset from a RTF from the prior
process and has now began matching its elements to this template -->
    <xsl:apply-templates select="NODE[LEVEL_ = 1]"
mode="groupTreeBranches"/><!-- NOTE: this template simply takes, once
again, our "NODE" elements that have a "LEVEL_" value of 1.  This will
allow us to then use recursion to access the remaining elements in the
group and reunite them with there long lost parent and potential
siblings -->
  </xsl:template>
  <xsl:template match="NODE" mode="groupTreeBranches"><!-- NOTE:  the
above template matches to this template (again, using match and mode to
ensure we are accessing the template intended for this particular pass
through the NODE elements-->
    <xsl:variable name="LEVEL_" select="LEVEL_"/><!-- NOTE:  a "LEVEL_"
variable is created to be used for comparisons in our parent/child
grouping process about to take place -->
    <xsl:variable name="SRCD" select="SRCD" /><!-- NOTE:  again, another
comparison variable is created, this time the value of the "SCRD"
element is set to the variable name "SCRD" (this doesnt create any
problems in our XPath because we access variables using the $ sign in
front telling our processor to access the variable value and not the
element value -->
    <xsl:element name="NODE"><!-- NOTE:  again, another NODE element is
created.  However this one will be a permanent fixture in our final
output where as our previous NODE element was created as a temporary,
yet structured, place holder of the values we want to access is this
processing of the final output -->
      <xsl:element name="SRCD"><!-- NOTE:  a "SRCD" element is created
in our output tree and the value set to the value of the current NODE
element in context -->
        <xsl:value-of select="SRCD"/><!-- NOTE:  value is set using
xsl:value-of to access the content found between the opening and closing
SRCD tags -->
      </xsl:element>
      <xsl:copy-of select="child::*[contains(name(), 'ELEMENT')]"/><!--
this process makes a copy of all child elements of our context node (in
this case literally NODE) that match the XPath test that using the
contains function and uses the name() function as the test to see if it
contains the sequence of characters "ELEMENT". If yes, it makes a copy
of the entire element and associated values to the output.  If no, it
continues testing each child element until there are no more to test.
Please note that this particular XPath test didnt include any reference
to text() or comment() (which are technically text() nodes) nodes and as
such any contained within the child structure of the containing NODE
element would not be tested by the processor.  -->
      <xsl:element name="CHILDREN"><!-- NOTE:  heres where we create our
CHILDREN element and the use XPath tests and recursion to process the
remaining NODE elements and place them with there proper parent and
respective siblings -->
        <xsl:apply-templates select="following-sibling::NODE[LEVEL_ =
$LEVEL_ + 1 and substring(SRCD, 1, string-length($SRCD)) = $SRCD]"
mode="groupTreeBranches"/><!-- NOTE:  this apply-templates uses
"following-sibling" as its axis for testing whether or not the next node
is a child of any prior NODE elements.  Two tests are performed and both
must pass for this element to become a child or sibling element to the
current NODE element in process.  The first adds a value of "1" to the
"LEVEL_" variable we created earlier in the process and then tests to
see if the value of this NODEs "LEVEL_" element is equal.  If it is then
we know that this is a potential candidate as a child node.  But were
not done yet.  Our XPath statement used the following-sibling axis with
no designation to position so our processor is checking EVERY following
sibling to see if its "LEVEL_" value is = the "LEVEL_" variable + 1.
Given that it is very likely that somewhere along the way a "LEVEL_"
element contained in a following NODE element will have a distance of
levels deep within the document that is the same as the "LEVEL_"
variable + 1 then this test alone will not be adequate.  Our next test
uses the string-length of the "SRCD" element of the first
preceding-sibling NODE element whos "LEVEL_" value is exactly one less
than that of the NODE in question and then uses that to trim the length
of the current CDFS element and then compares the two.  If this
preceding-element is truly the parent of this element in question then
their "genetic" sequence should match.  For example, if the previous
matching element had a value of 3,1 for SRCD then for our element to be
a child he/she too would have to have 3,1 as his/her first two values.
If he/she does, WHAMO, a family had bee reunited.  If not, the quest (at
least for this NODE element) to find his long lost family members must
continue during the next pass through yet one more temporary foster
home-->
      </xsl:element><!-- every match made using the XPath test above
will be processed within the context of this "CHILDREN" element.  Given
that we use the same match element ("NODE") and the same mode
("groupTreeBranches") as the xsl:template it is contained in then each
matching node will also go through this quest to find there long lost
children.  As such this template has created a recursive process that
will continue without hesitation or concern for anything but bringing
back together each familys mom, dad, father, son, mother, daughter,
sister, brother, grandm.... ok, ill stop ;) -->
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

-----Original Message-----
From: Jim Stoll [mailto:jestoll@xxxxxxxxxx] 
Sent: Friday, March 26, 2004 10:06 PM
To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
Subject: [xsl] Advice/feedback on stylesheet?

All,
I'm an XSL Newbie again (last used it about 2 years ago).  Anyway, I'm
looking for some review/feedback on a relatively simple stylesheet.  As
background, I'm executing an SQL query against an Oracle database, using
Oracle's CONNECT BY functionality to bring back a hierarchical result
set. I'm then transforming the results into XML via Oracle 9.1's
SYS_XMLAGG and SYS_XMLGEN functionality (largely unimportant, but I
mention it in case it helps anyone), which brings me back a 'flat' XML
representation of the previously-hierarchical result set. (This version
of Oracle isn't smart enough to maintain the hierarchical nature of the
data through the XML conversion - newer versions are, but this is the
version I have to use...) So, I end up w/ XML elements that are in the
proper 'order', but not nested. The XSL stylesheet then nests things
appropriately based on ROWID values (unique id's for each row in the
resultset) for each node and its parent in the hierarchical
relationship, as!
  well as a LEVEL attribute returned from the query.  (XML, XSL and
output are included below).

Anyway, I'd like to know if/how I can do this in a smarter manner, if
I've made any serious mis-steps or such, if this approach is likely to
bring the machine to its knees for large datasets, etc. (this will be
occurring on the database server).

Other specific questions:
 - thoughts on having two templates for the nodes to separate out
top-level nodes, or if one template w/ an xsl:if would be better
 - the prevailing philosophy as regards whether to have an empty
CHILDREN element (see below) if there are no children or not
 - I've tried to make this as generic as possible, as I have opportunity
to apply it to numerous situations, but have been unable to find a way
to parameterize the ROWSET_, ROWID_ and PARENTROWID_ tag names (ie, to
allow the user to set these to another value in their database query and
pass parameters to the stylesheet naming their substituted values) - if
anyone has any thoughts on this, I'd be very interested to hear them, as
well. (It works as-is, and is an acceptable work-around, but I'd at
least like to know if its possible.)

All advice is appreciated! (and yes, lets establish up-front, I'm an
idiot... That's why I'm here... <:-)

Thanks Very Much!!

Jim Stoll

<<input XML>>
<?xml version="1.0"?>
<ROWSET>
<NODE>
  <LEVEL_>1</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAG</ROWID_>
  <SRCD>1</SRCD>
  <ELEMENT1>1e1</ELEMENT1>
  <ELEMENT2>1e2</ELEMENT2>
  <ELEMENT3>1e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>1</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAH</ROWID_>
  <SRCD>2</SRCD>
  <ELEMENT1>2e1</ELEMENT1>
  <ELEMENT2>2e2</ELEMENT2>
  <ELEMENT3>2e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>2</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAI</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAH</PARENTROWID_>
  <SRCD>2,1</SRCD>
  <ELEMENT1>21e1</ELEMENT1>
  <ELEMENT2>21e2</ELEMENT2>
  <ELEMENT3>21e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>3</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAJ</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAI</PARENTROWID_>
  <SRCD>2,1,1</SRCD>
  <ELEMENT1>211e1</ELEMENT1>
  <ELEMENT2>211e2</ELEMENT2>
  <ELEMENT3>211e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>3</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAK</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAI</PARENTROWID_>
  <SRCD>2,1,2</SRCD>
  <ELEMENT1>212e1</ELEMENT1>
  <ELEMENT2>212e2</ELEMENT2>
  <ELEMENT3>212e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>1</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAL</ROWID_>
  <SRCD>3</SRCD>
  <ELEMENT1>3e1</ELEMENT1>
  <ELEMENT2>3e2</ELEMENT2>
  <ELEMENT3>3e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>2</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAM</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAL</PARENTROWID_>
  <SRCD>3,1</SRCD>
  <ELEMENT1>31e1</ELEMENT1>
  <ELEMENT2>31e2</ELEMENT2>
  <ELEMENT3>31e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>2</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAN</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAL</PARENTROWID_>
  <SRCD>3,2</SRCD>
  <ELEMENT1>32e1</ELEMENT1>
  <ELEMENT2>32e2</ELEMENT2>
  <ELEMENT3>32e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>3</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAO</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAN</PARENTROWID_>
  <SRCD>3,2,1</SRCD>
  <ELEMENT1>321e1</ELEMENT1>
  <ELEMENT2>321e2</ELEMENT2>
  <ELEMENT3>321e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>2</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAP</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAL</PARENTROWID_>
  <SRCD>3,3</SRCD>
  <ELEMENT1>33e1</ELEMENT1>
  <ELEMENT2>33e2</ELEMENT2>
  <ELEMENT3>33e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>3</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAQ</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAP</PARENTROWID_>
  <SRCD>3,3,1</SRCD>
  <ELEMENT1>331e1</ELEMENT1>
  <ELEMENT2>331e2</ELEMENT2>
  <ELEMENT3>331e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>4</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAR</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAQ</PARENTROWID_>
  <SRCD>3,3,1,1</SRCD>
  <ELEMENT1>3311e1</ELEMENT1>
  <ELEMENT2>3311e2</ELEMENT2>
  <ELEMENT3>3311e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>1</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAS</ROWID_>
  <SRCD>4</SRCD>
  <ELEMENT1>4e1</ELEMENT1>
  <ELEMENT2>4e2</ELEMENT2>
  <ELEMENT3>4e3</ELEMENT3>
</NODE>
</ROWSET>

<<XSL>>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
version="1.0">

	<!--
		Preconditions on the database-generated XML processed by
this stylesheet:
			- there will always be a root element named
ROWSET, that is not part of the 'data' hierarchy (ie, its part of the
XML hierarchy, but not part of the original relational data hierarchy)
			- all 'data' elements will be siblings
underneath the root element (result of the 'flattening' nature of the
database's result-set-to-XML conversion)
			- every 'data' element will have a child
'utility' element named LEVEL_ that indicates the nesting level in the
original data hierarchy, where 1 is a top-level element (ie, no parent
in the data hierarchy)
			- every 'data' element will have a child
'utility' element named ROWID_ that is a unique identifier for the row
in the original data hierarchy
			- every data element that is a child of another
data element will have a child  'utility' element named PARENTROWID_
that identifies the data element's parent
	-->
						
	<!--while database-generated XML will always be contained in a
root node named ROWSET, the user can choose whether to include a root
tag in the output, and if so, what that tag will be named - defaults to
ROWSET-->
	<xsl:param name="root-tag-param">ROWSET</xsl:param>
	
	<!--if a node has children, they will be grouped in a wrapper
tag, but the user can specify the wrapper tag name if desired - defaults
to CHILDREN-->
	<xsl:param name="children-tag-param">CHILDREN</xsl:param>
	
	<!--by default, the mandatory LEVEL_ and ROWID_ tags provided in
the database-generated XML (and required for this stylesheet's
operations) will not be included in the output, but can be if desired-->
	<xsl:param name="output-level-tag-param">false</xsl:param>
	<xsl:param name="output-rowid-tag-param">false</xsl:param>
	<xsl:param
name="output-parent-rowid-tag-param">false</xsl:param>

	<!--recall precondition that the database-generated XML will
always have a root element named ROWSET, and that all 'data' elements
will be siblings under the root, so map all nodes to their parent-->
	<xsl:key name="key-nodes-by-parent" match="/ROWSET/*"
use="PARENTROWID_"/>
	
	<xsl:template match="/">
		<!--if a root tag value is provided, use it,otherwise,
just produce a fragment-->
		<xsl:if test="$root-tag-param!=''">
			<xsl:text
disable-output-escaping="yes">&lt;</xsl:text><xsl:value-of
select="$root-tag-param"/><xsl:text
disable-output-escaping="yes">&gt;</xsl:text>
		</xsl:if>
			<!--always want to process the document though,
regardless of whether a root tag is to be output or not!-->
			<xsl:apply-templates/>
		<xsl:if test="$root-tag-param!=''">
			<xsl:text
disable-output-escaping="yes">&lt;/</xsl:text><xsl:value-of
select="$root-tag-param"/><xsl:text
disable-output-escaping="yes">&gt;</xsl:text>
		</xsl:if>
	</xsl:template>
	
	<!--all top-level parents will have a level of 1 - only
match/process these, as they will in turn process their children via the
key map-->
	<xsl:template match="*[LEVEL_='1']">
		<xsl:call-template name="duplicate-node">
			<xsl:with-param name="duplicatee-param"
select="."/>
		</xsl:call-template>
	</xsl:template>
	
	<!--don't match children(any node w/ a level != 1) - as they'll
be processed by their parent-->
	<xsl:template match="*[LEVEL_!='1']">
	</xsl:template>
	
	<!--for each node - either top-level parents resulting from a
match, or children resulting from key iteration - duplicate the node,
its children and attributes-->
	<xsl:template name="duplicate-node">
		<xsl:param name="duplicatee-param"/>
		<xsl:copy>
			<xsl:copy-of select="@*"/>
			<!--output  the 'utility' elements if/as
specified by params-->
			<xsl:copy-of select="*[(not(self::thisElement))
and (not(name()='LEVEL_') or (name()='LEVEL_' and
$output-level-tag-param='true')) and (not(name()='ROWID_') or
(name()='ROWID_' and $output-rowid-tag-param='true')) and
(not(name()='PARENTROWID_') or (name()='PARENTROWID_' and
$output-parent-rowid-tag-param='true'))]"/>
			<xsl:element name="{$children-tag-param}">
				<xsl:for-each
select="key('key-nodes-by-parent', ROWID_)">
					<xsl:call-template
name="duplicate-node">
						<xsl:with-param
name="duplicatee-param" select="."/>
					</xsl:call-template>
				</xsl:for-each>
			</xsl:element>
		</xsl:copy>
	</xsl:template>
</xsl:stylesheet>

<<output XML>>
<?xml version="1.0" encoding="UTF-8"?>
<ROWSET>
	<NODE>
		<SRCD>1</SRCD>
		<ELEMENT1>1e1</ELEMENT1>
		<ELEMENT2>1e2</ELEMENT2>
		<ELEMENT3>1e3</ELEMENT3>
		<CHILDREN />
	</NODE>
	<NODE>
		<SRCD>2</SRCD>
		<ELEMENT1>2e1</ELEMENT1>
		<ELEMENT2>2e2</ELEMENT2>
		<ELEMENT3>2e3</ELEMENT3>
		<CHILDREN>
			<NODE>
				<SRCD>2,1</SRCD>
				<ELEMENT1>21e1</ELEMENT1>
				<ELEMENT2>21e2</ELEMENT2>
				<ELEMENT3>21e3</ELEMENT3>
				<CHILDREN>
					<NODE>
						<SRCD>2,1,1</SRCD>
	
<ELEMENT1>211e1</ELEMENT1>
	
<ELEMENT2>211e2</ELEMENT2>
	
<ELEMENT3>211e3</ELEMENT3>
						<CHILDREN />
					</NODE>
					<NODE>
						<SRCD>2,1,2</SRCD>
	
<ELEMENT1>212e1</ELEMENT1>
	
<ELEMENT2>212e2</ELEMENT2>
	
<ELEMENT3>212e3</ELEMENT3>
						<CHILDREN />
					</NODE>
				</CHILDREN>
			</NODE>
		</CHILDREN>
	</NODE>
	<NODE>
		<SRCD>3</SRCD>
		<ELEMENT1>3e1</ELEMENT1>
		<ELEMENT2>3e2</ELEMENT2>
		<ELEMENT3>3e3</ELEMENT3>
		<CHILDREN>
			<NODE>
				<SRCD>3,1</SRCD>
				<ELEMENT1>31e1</ELEMENT1>
				<ELEMENT2>31e2</ELEMENT2>
				<ELEMENT3>31e3</ELEMENT3>
				<CHILDREN />
			</NODE>
			<NODE>
				<SRCD>3,2</SRCD>
				<ELEMENT1>32e1</ELEMENT1>
				<ELEMENT2>32e2</ELEMENT2>
				<ELEMENT3>32e3</ELEMENT3>
				<CHILDREN>
					<NODE>
						<SRCD>3,2,1</SRCD>
	
<ELEMENT1>321e1</ELEMENT1>
	
<ELEMENT2>321e2</ELEMENT2>
	
<ELEMENT3>321e3</ELEMENT3>
						<CHILDREN />
					</NODE>
				</CHILDREN>
			</NODE>
			<NODE>
				<SRCD>3,3</SRCD>
				<ELEMENT1>33e1</ELEMENT1>
				<ELEMENT2>33e2</ELEMENT2>
				<ELEMENT3>33e3</ELEMENT3>
				<CHILDREN>
					<NODE>
						<SRCD>3,3,1</SRCD>
	
<ELEMENT1>331e1</ELEMENT1>
	
<ELEMENT2>331e2</ELEMENT2>
	
<ELEMENT3>331e3</ELEMENT3>
						<CHILDREN>
							<NODE>
	
<SRCD>3,3,1,1</SRCD>
	
<ELEMENT1>3311e1</ELEMENT1>
	
<ELEMENT2>3311e2</ELEMENT2>
	
<ELEMENT3>3311e3</ELEMENT3>
	
<CHILDREN />
							</NODE>
						</CHILDREN>
					</NODE>
				</CHILDREN>
			</NODE>
		</CHILDREN>
	</NODE>
	<NODE>
		<SRCD>4</SRCD>
		<ELEMENT1>4e1</ELEMENT1>
		<ELEMENT2>4e2</ELEMENT2>
		<ELEMENT3>4e3</ELEMENT3>
		<CHILDREN />
	</NODE>
</ROWSET>

Current Thread