KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xalan > transformer > KeyTable


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 /*
17  * $Id: KeyTable.java,v 1.17 2004/02/16 20:41:29 minchau Exp $
18  */

19 package org.apache.xalan.transformer;
20
21 import java.util.Hashtable JavaDoc;
22 import java.util.Vector JavaDoc;
23
24 import javax.xml.transform.TransformerException JavaDoc;
25
26 import org.apache.xalan.templates.KeyDeclaration;
27 import org.apache.xml.dtm.DTM;
28 import org.apache.xml.dtm.DTMIterator;
29 import org.apache.xml.utils.PrefixResolver;
30 import org.apache.xml.utils.QName;
31 import org.apache.xml.utils.WrappedRuntimeException;
32 import org.apache.xml.utils.XMLString;
33 import org.apache.xpath.XPathContext;
34 import org.apache.xpath.objects.XNodeSet;
35 import org.apache.xpath.objects.XObject;
36
37 /**
38  * Table of element keys, keyed by document node. An instance of this
39  * class is keyed by a Document node that should be matched with the
40  * root of the current context.
41  * @xsl.usage advanced
42  */

43 public class KeyTable
44 {
45   /**
46    * The document key. This table should only be used with contexts
47    * whose Document roots match this key.
48    */

49   private int m_docKey;
50
51   /**
52    * Vector of KeyDeclaration instances holding the key declarations.
53    */

54   private Vector JavaDoc m_keyDeclarations;
55
56   /**
57    * Hold a cache of key() function result for each ref.
58    * Key is XMLString, the ref value
59    * Value is XNodeSet, the key() function result for the given ref value.
60    */

61   private Hashtable JavaDoc m_refsTable = null;
62
63   /**
64    * Get the document root matching this key.
65    *
66    * @return the document root matching this key
67    */

68   public int getDocKey()
69   {
70     return m_docKey;
71   }
72
73   /**
74    * The main iterator that will walk through the source
75    * tree for this key.
76    */

77   private XNodeSet m_keyNodes;
78   
79   KeyIterator getKeyIterator()
80   {
81     return (KeyIterator)(m_keyNodes.getContainedIter());
82   }
83
84   /**
85    * Build a keys table.
86    * @param doc The owner document key.
87    * @param nscontext The stylesheet's namespace context.
88    * @param name The key name
89    * @param keyDeclarations The stylesheet's xsl:key declarations.
90    * @param xmlLiaison The parser liaison for support of getNodeData(useNode).
91    *
92    * @throws javax.xml.transform.TransformerException
93    */

94   public KeyTable(
95           int doc, PrefixResolver nscontext, QName name, Vector JavaDoc keyDeclarations, XPathContext xctxt)
96             throws javax.xml.transform.TransformerException JavaDoc
97   {
98     m_docKey = doc;
99     m_keyDeclarations = keyDeclarations;
100     KeyIterator ki = new KeyIterator(name, keyDeclarations);
101
102     m_keyNodes = new XNodeSet(ki);
103     m_keyNodes.allowDetachToRelease(false);
104     m_keyNodes.setRoot(doc, xctxt);
105   }
106
107   /**
108    * Given a valid element key, return the corresponding node list.
109    *
110    * @param name The name of the key, which must match the 'name' attribute on xsl:key.
111    * @param ref The value that must match the value found by the 'match' attribute on xsl:key.
112    * @return a set of nodes referenced by the key named <CODE>name</CODE> and the reference <CODE>ref</CODE>. If no node is referenced by this key, an empty node set is returned.
113    */

114   public XNodeSet getNodeSetDTMByKey(QName name, XMLString ref)
115
116   {
117     XNodeSet refNodes = (XNodeSet) getRefsTable().get(ref);
118     // clone wiht reset the node set
119
try
120     {
121       if (refNodes != null)
122       {
123          refNodes = (XNodeSet) refNodes.cloneWithReset();
124        }
125     }
126     catch (CloneNotSupportedException JavaDoc e)
127     {
128       refNodes = null;
129     }
130
131     if (refNodes == null) {
132      // create an empty XNodeSet
133
KeyIterator ki = (KeyIterator) (m_keyNodes).getContainedIter();
134       XPathContext xctxt = ki.getXPathContext();
135       refNodes = new XNodeSet(xctxt.getDTMManager()) {
136         public void setRoot(int nodeHandle, Object JavaDoc environment) {
137           // Root cannot be set on non-iterated node sets. Ignore it.
138
}
139       };
140       refNodes.reset();
141     }
142
143     return refNodes;
144   }
145
146   /**
147    * Get Key Name for this KeyTable
148    *
149    * @return Key name
150    */

151   public QName getKeyTableName()
152   {
153     return getKeyIterator().getName();
154   }
155
156   /**
157    * @return key declaration for the key associated to this KeyTable
158    */

159   private KeyDeclaration getKeyDeclaration() {
160     int nDeclarations = m_keyDeclarations.size();
161
162     // Walk through each of the declarations made with xsl:key
163
for (int i = 0; i < nDeclarations; i++)
164     {
165       KeyDeclaration kd = (KeyDeclaration) m_keyDeclarations.elementAt(i);
166
167       // Only continue if the name on this key declaration
168
// matches the name on the iterator for this walker.
169
if (kd.getName().equals(getKeyTableName()))
170       {
171         return kd;
172       }
173     }
174
175     // should never happen
176
return null;
177   }
178
179   /**
180    * @return lazy initialized refs table associating evaluation of key function
181    * with a XNodeSet
182    */

183   private Hashtable JavaDoc getRefsTable()
184   {
185     if (m_refsTable == null)
186     {
187       m_refsTable = new Hashtable JavaDoc(89); // initial capacity set to a prime number to improve hash algorithm performance
188

189       KeyIterator ki = (KeyIterator) (m_keyNodes).getContainedIter();
190       XPathContext xctxt = ki.getXPathContext();
191
192       KeyDeclaration keyDeclaration = getKeyDeclaration();
193
194       int currentNode;
195       m_keyNodes.reset();
196       while (DTM.NULL != (currentNode = m_keyNodes.nextNode()))
197       {
198         try
199         {
200           XObject xuse = keyDeclaration.getUse().execute(xctxt, currentNode, ki.getPrefixResolver());
201
202           if (xuse.getType() != xuse.CLASS_NODESET)
203           {
204             XMLString exprResult = xuse.xstr();
205             addValueInRefsTable(xctxt, exprResult, currentNode);
206           }
207           else
208           {
209             DTMIterator i = ((XNodeSet)xuse).iterRaw();
210             int currentNodeInUseClause;
211
212             while (DTM.NULL != (currentNodeInUseClause = i.nextNode()))
213             {
214               DTM dtm = xctxt.getDTM(currentNodeInUseClause);
215               XMLString exprResult = dtm.getStringValue(currentNodeInUseClause);
216               addValueInRefsTable(xctxt, exprResult, currentNode);
217             }
218           }
219         }
220         catch (TransformerException JavaDoc te)
221         {
222           throw new WrappedRuntimeException(te);
223         }
224       }
225     }
226     return m_refsTable;
227   }
228
229   /**
230    * Add an association between a ref and a node in the m_refsTable.
231    * Requires that m_refsTable != null
232    * @param xctxt XPath context
233    * @param ref the value of the use clause of the current key for the given node
234    * @param node the node to reference
235    */

236   private void addValueInRefsTable(XPathContext xctxt, XMLString ref, int node) {
237     
238     XNodeSet nodes = (XNodeSet) m_refsTable.get(ref);
239     if (nodes == null)
240     {
241       nodes = new XNodeSet(node, xctxt.getDTMManager());
242       nodes.nextNode();
243       m_refsTable.put(ref, nodes);
244     }
245     else
246     {
247       // Nodes are passed to this method in document order. Since we need to
248
// suppress duplicates, we only need to check against the last entry
249
// in each nodeset. We use nodes.nextNode after each entry so we can
250
// easily compare node against the current node.
251
if (nodes.getCurrentNode() != node) {
252           nodes.mutableNodeset().addNode(node);
253           nodes.nextNode();
254       }
255     }
256   }
257 }
258
Popular Tags