blob: aa137f67d04950e3a5a976c0eb0e3281ac40e2ce [file] [log] [blame]
<?xml version="1.0" standalone="no"?>
<!DOCTYPE s1 SYSTEM "../../style/dtd/document.dtd">
<!--
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2001, Sun
* Microsystems., http://www.sun.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
-->
<s1 title="&lt;xsl:key&gt; / key() / KeyPattern">
<ul>
<li><link anchor="functionality">Functionality</link></li>
<li><link anchor="implementation">Implementation</link></li>
</ul>
<anchor name="functionality"/>
<s2 title="Functionality">
<p>The <code>&lt;xsl:key&gt;</code> element is a top-level element that can be
used to define a named index of nodes from the source XML tree(s). The
element has three attributes:</p>
<ul>
<li>
<code>name</code> - the name of the index
</li>
<li>
<code>match</code> - a pattern that defines the nodeset we want
indexed
</li>
<li>
<code>use</code> - an expression that defines the value to be used
as the index key value.
</li>
</ul>
<p>A named index can be accessed using either the <code>key()</code> function or
a KeyPattern. Both these methods address the index using its defined name
(the "name" attribute above) and a parameter defining one or more lookup
values for the index. The function or pattern returns a node set containing
all nodes in the index whose key value match the parameter's value(s):</p>
<source>
&lt;xsl:key name="book-author" match="book" use="author"/&gt;
:
:
&lt;xsl:for-each select="key('book-author', 'Mikhail Bulgakov')"&gt;
&lt;xsl:value-of select="author"/&gt;
&lt;xsl:text&gt;: &lt;/xsl:text&gt;
&lt;xsl:value-of select="author"/&gt;
&lt;xsl:text&gt;&amp;#xa;&lt;/xsl:text&gt;
&lt;/xsl:for-each&gt;
</source>
<p>The KeyPattern can be used within an index definition to create unions
and intersections of node sets:</p>
<source>
&lt;xsl:key name="cultcies" match="farmer | fisherman" use="name"/&gt;
</source>
<p>This could of course be done using regular <code>&lt;xsl:for-each&gt;</code>
and <code>&lt;xsl:select&gt;</code> elements. However, if your stylesheet
accesses the same selection of nodes over and over again, the transformation
will be much more efficient using pre-indexed keys as shown above.</p>
</s2><anchor name="implementation"/>
<s2 title="Implementation">
<p>AbstractTranslet has a global hashtable that holds an index for each named
key in the stylesheet (hashing on the "name" attribute of xsl:key).
AbstractTranslet has a couple of public methods for inserting and retrieving
data from this hashtable:</p>
<source>
public void buildKeyIndex(String name, int nodeID, String value);
public KeyIndex KeyIndex getKeyIndex(String name);
</source>
<p>The Key class compiles code that traverses the input DOM and extracts
nodes that match some given parameters (the <code>"match"</code> attribute of the
<code>&lt;xsl:key&gt;</code> element). A new element is inserted into the named
key's index. The nodes' DOM index and the value translated from the
<code>"use"</code> attribute of the <code>&lt;xsl:key&gt;</code> element are stored
in the new entry in the index.</p>
<p>The index itself is implemented in the <code>KeyIndex</code> class.
The index has an hashtable with all the values from the matching nodes (the
part of the node used to generate this value is the one specified in the
<code>"use"</code> attribute). For every matching value there is a Vector
holding a list of all node indexes for which this value gives a match:</p>
<p><img src="key_relations.gif" alt="key_relations.gif"/></p>
<p><ref>Figure 1: Indexing tables</ref></p>
<p>The <code>KeyIndex</code> class implements the <code>NodeIterator</code>
interface, so that it can be returned directly by the implementation of the
<code>key()</code> function. This is how the index generated by
<code>&lt;xsl:key&gt;</code> and the node-set returned by the <code>key()</code> and
KeyPattern are tied together. You can see how this is done in the
<code>translate()</code> method of the <code>KeyCall</code> class.</p>
<p>The <code>key()</code> function can be called in two ways:</p>
<source>
key('key-name','value')
key('key-name','node-set')
</source>
<p>The first parameter is always the name of the key. We use this value to
lookup our index from the _keyIndexes hashtable in AbstractTranslet:</p>
<source>
il.append(classGen.aloadThis());
_name.translate(classGen, methodGen);
il.append(new INVOKEVIRTUAL(getKeyIndex));
</source>
<p>This compiles into a call to
<code>AbstractTranslet.getKeyIndex(String name)</code>, and it leaves a
<code>KeyIndex</code> object on the stack. What we then need to do it to
initialise the <code>KeyIndex</code> to give us nodes with the requested value.
This is done by leaving the <code>KeyIndex</code> object on the stack and pushing
the <code>"value"</code> parameter to <code>key()</code>, before calling
<code>lookup()</code> on the index:</p>
<source>
il.append(DUP); // duplicate the KeyIndex obejct before return
_value.translate(classGen, methodGen);
il.append(new INVOKEVIRTUAL(lookup));
</source>
<p>This compiles into a call to <code>KeyIndex.lookup(String value)</code>. This
will initialise the <code>KeyIndex</code> object to return nodes that match the
given value, so the <code>KeyIndex</code> object can be left on the stack when
we return. This because the <code>KeyIndex</code> object implements the
<code>NodeIterator</code> interface.</p>
<p>This matter is a bit more complex when the second parameter of
<code>key()</code> is a node-set. In this case we need to traverse the nodes in
the set and do a lookup for each node in the set. What I do is this:</p>
<ul>
<li>
construct a <code>KeyIndex</code> object that will hold the return node-set
</li>
<li>
find the named <code>KeyIndex</code> object from the hashtable in
AbstractTranslet
</li>
<li>
get an iterator for the node-set and do the folowing loop:</li>
<ul>
<li>get string value for current node</li>
<li>do lookup in KeyIndex object for the named index</li>
<li>merge the resulting node-set into the return node-set</li>
</ul>
<li>
leave the return node-set on stack when done
</li>
</ul>
<p>The only work that remains is to update the <code>merge()</code> method of
the <code>KeyIndex</code> class so that it eliminates duplicate nodes in the
resulting node-set.</p>
</s2>
</s1>