• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id: KeyTable.java 468645 2006-10-28 06:57:24Z minchau $
20  */
21 package org.apache.xalan.transformer;
22 
23 import java.util.Hashtable;
24 import java.util.Vector;
25 
26 import javax.xml.transform.TransformerException;
27 
28 import org.apache.xalan.templates.KeyDeclaration;
29 import org.apache.xml.dtm.DTM;
30 import org.apache.xml.dtm.DTMIterator;
31 import org.apache.xml.utils.PrefixResolver;
32 import org.apache.xml.utils.QName;
33 import org.apache.xml.utils.WrappedRuntimeException;
34 import org.apache.xml.utils.XMLString;
35 import org.apache.xpath.XPathContext;
36 import org.apache.xpath.objects.XNodeSet;
37 import org.apache.xpath.objects.XObject;
38 
39 /**
40  * Table of element keys, keyed by document node.  An instance of this
41  * class is keyed by a Document node that should be matched with the
42  * root of the current context.
43  * @xsl.usage advanced
44  */
45 public class KeyTable
46 {
47   /**
48    * The document key.  This table should only be used with contexts
49    * whose Document roots match this key.
50    */
51   private int m_docKey;
52 
53   /**
54    * Vector of KeyDeclaration instances holding the key declarations.
55    */
56   private Vector m_keyDeclarations;
57 
58   /**
59    * Hold a cache of key() function result for each ref.
60    * Key is XMLString, the ref value
61    * Value is XNodeSet, the key() function result for the given ref value.
62    */
63   private Hashtable m_refsTable = null;
64 
65   /**
66    * Get the document root matching this key.
67    *
68    * @return the document root matching this key
69    */
getDocKey()70   public int getDocKey()
71   {
72     return m_docKey;
73   }
74 
75   /**
76    * The main iterator that will walk through the source
77    * tree for this key.
78    */
79   private XNodeSet m_keyNodes;
80 
getKeyIterator()81   KeyIterator getKeyIterator()
82   {
83   	return (KeyIterator)(m_keyNodes.getContainedIter());
84   }
85 
86   /**
87    * Build a keys table.
88    * @param doc The owner document key.
89    * @param nscontext The stylesheet's namespace context.
90    * @param name The key name
91    * @param keyDeclarations The stylesheet's xsl:key declarations.
92    *
93    * @throws javax.xml.transform.TransformerException
94    */
KeyTable( int doc, PrefixResolver nscontext, QName name, Vector keyDeclarations, XPathContext xctxt)95   public KeyTable(
96           int doc, PrefixResolver nscontext, QName name, Vector keyDeclarations, XPathContext xctxt)
97             throws javax.xml.transform.TransformerException
98   {
99     m_docKey = doc;
100     m_keyDeclarations = keyDeclarations;
101     KeyIterator ki = new KeyIterator(name, keyDeclarations);
102 
103     m_keyNodes = new XNodeSet(ki);
104     m_keyNodes.allowDetachToRelease(false);
105     m_keyNodes.setRoot(doc, xctxt);
106   }
107 
108   /**
109    * Given a valid element key, return the corresponding node list.
110    *
111    * @param name The name of the key, which must match the 'name' attribute on xsl:key.
112    * @param ref The value that must match the value found by the 'match' attribute on xsl:key.
113    * @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.
114    */
getNodeSetDTMByKey(QName name, XMLString ref)115   public XNodeSet getNodeSetDTMByKey(QName name, XMLString ref)
116 
117   {
118     XNodeSet refNodes = (XNodeSet) getRefsTable().get(ref);
119     // clone wiht reset the node set
120    try
121     {
122       if (refNodes != null)
123       {
124          refNodes = (XNodeSet) refNodes.cloneWithReset();
125        }
126     }
127     catch (CloneNotSupportedException e)
128     {
129       refNodes = null;
130     }
131 
132     if (refNodes == null) {
133      //  create an empty XNodeSet
134       KeyIterator ki = (KeyIterator) (m_keyNodes).getContainedIter();
135       XPathContext xctxt = ki.getXPathContext();
136       refNodes = new XNodeSet(xctxt.getDTMManager()) {
137         public void setRoot(int nodeHandle, Object environment) {
138           // Root cannot be set on non-iterated node sets. Ignore it.
139         }
140       };
141       refNodes.reset();
142     }
143 
144     return refNodes;
145   }
146 
147   /**
148    * Get Key Name for this KeyTable
149    *
150    * @return Key name
151    */
getKeyTableName()152   public QName getKeyTableName()
153   {
154     return getKeyIterator().getName();
155   }
156 
157   /**
158    * @return key declarations for the key associated to this KeyTable
159    */
getKeyDeclarations()160   private Vector getKeyDeclarations() {
161     int nDeclarations = m_keyDeclarations.size();
162     Vector keyDecls = new Vector(nDeclarations);
163 
164     // Walk through each of the declarations made with xsl:key
165     for (int i = 0; i < nDeclarations; i++)
166     {
167       KeyDeclaration kd = (KeyDeclaration) m_keyDeclarations.elementAt(i);
168 
169       // Add the declaration if the name on this key declaration
170       // matches the name on the iterator for this walker.
171       if (kd.getName().equals(getKeyTableName())) {
172         keyDecls.add(kd);
173       }
174     }
175 
176     return keyDecls;
177   }
178 
179   /**
180    * @return lazy initialized refs table associating evaluation of key function
181    *         with a XNodeSet
182    */
getRefsTable()183   private Hashtable getRefsTable()
184   {
185     if (m_refsTable == null) {
186       // initial capacity set to a prime number to improve hash performance
187       m_refsTable = new Hashtable(89);
188 
189       KeyIterator ki = (KeyIterator) (m_keyNodes).getContainedIter();
190       XPathContext xctxt = ki.getXPathContext();
191 
192       Vector keyDecls = getKeyDeclarations();
193       int nKeyDecls = keyDecls.size();
194 
195       int currentNode;
196       m_keyNodes.reset();
197       while (DTM.NULL != (currentNode = m_keyNodes.nextNode()))
198       {
199         try
200         {
201           for (int keyDeclIdx = 0; keyDeclIdx < nKeyDecls; keyDeclIdx++) {
202             KeyDeclaration keyDeclaration =
203                 (KeyDeclaration) keyDecls.elementAt(keyDeclIdx);
204             XObject xuse =
205                 keyDeclaration.getUse().execute(xctxt,
206                                                 currentNode,
207                                                 ki.getPrefixResolver());
208 
209             if (xuse.getType() != xuse.CLASS_NODESET) {
210               XMLString exprResult = xuse.xstr();
211               addValueInRefsTable(xctxt, exprResult, currentNode);
212             } else {
213               DTMIterator i = ((XNodeSet)xuse).iterRaw();
214               int currentNodeInUseClause;
215 
216               while (DTM.NULL != (currentNodeInUseClause = i.nextNode())) {
217                 DTM dtm = xctxt.getDTM(currentNodeInUseClause);
218                 XMLString exprResult =
219                     dtm.getStringValue(currentNodeInUseClause);
220                 addValueInRefsTable(xctxt, exprResult, currentNode);
221               }
222             }
223           }
224         } catch (TransformerException te) {
225           throw new WrappedRuntimeException(te);
226         }
227       }
228     }
229     return m_refsTable;
230   }
231 
232   /**
233    * Add an association between a ref and a node in the m_refsTable.
234    * Requires that m_refsTable != null
235    * @param xctxt XPath context
236    * @param ref the value of the use clause of the current key for the given node
237    * @param node the node to reference
238    */
addValueInRefsTable(XPathContext xctxt, XMLString ref, int node)239   private void addValueInRefsTable(XPathContext xctxt, XMLString ref, int node) {
240 
241     XNodeSet nodes = (XNodeSet) m_refsTable.get(ref);
242     if (nodes == null)
243     {
244       nodes = new XNodeSet(node, xctxt.getDTMManager());
245       nodes.nextNode();
246       m_refsTable.put(ref, nodes);
247     }
248     else
249     {
250       // Nodes are passed to this method in document order.  Since we need to
251       // suppress duplicates, we only need to check against the last entry
252       // in each nodeset.  We use nodes.nextNode after each entry so we can
253       // easily compare node against the current node.
254       if (nodes.getCurrentNode() != node) {
255           nodes.mutableNodeset().addNode(node);
256           nodes.nextNode();
257       }
258     }
259   }
260 }
261