| <?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="<xsl:key> / 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><xsl:key></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> |
| <xsl:key name="book-author" match="book" use="author"/> |
| : |
| : |
| <xsl:for-each select="key('book-author', 'Mikhail Bulgakov')"> |
| <xsl:value-of select="author"/> |
| <xsl:text>: </xsl:text> |
| <xsl:value-of select="author"/> |
| <xsl:text>&#xa;</xsl:text> |
| </xsl:for-each> |
| </source> |
| |
| <p>The KeyPattern can be used within an index definition to create unions |
| and intersections of node sets:</p> |
| |
| <source> |
| <xsl:key name="cultcies" match="farmer | fisherman" use="name"/> |
| </source> |
| |
| <p>This could of course be done using regular <code><xsl:for-each></code> |
| and <code><xsl:select></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><xsl:key></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><xsl:key></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><xsl:key></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> |