1. /*
  2. * Copyright 2001-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: Key.java,v 1.18 2004/02/24 03:55:47 zongaro Exp $
  18. */
  19. package com.sun.org.apache.xalan.internal.xsltc.compiler;
  20. import com.sun.org.apache.bcel.internal.generic.BranchHandle;
  21. import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  22. import com.sun.org.apache.bcel.internal.generic.GOTO;
  23. import com.sun.org.apache.bcel.internal.generic.IFEQ;
  24. import com.sun.org.apache.bcel.internal.generic.IFGE;
  25. import com.sun.org.apache.bcel.internal.generic.IFGT;
  26. import com.sun.org.apache.bcel.internal.generic.ILOAD;
  27. import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  28. import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  29. import com.sun.org.apache.bcel.internal.generic.ISTORE;
  30. import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
  31. import com.sun.org.apache.bcel.internal.generic.InstructionList;
  32. import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  33. import com.sun.org.apache.bcel.internal.generic.PUSH;
  34. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  35. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  36. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  37. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSetType;
  38. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType;
  39. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  40. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  41. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  42. import com.sun.org.apache.xalan.internal.xsltc.dom.Axis;
  43. import com.sun.org.apache.xml.internal.utils.XMLChar;
  44. /**
  45. * @author Morten Jorgensen
  46. * @author Santiago Pericas-Geertsen
  47. */
  48. final class Key extends TopLevelElement {
  49. /**
  50. * The name of this key as defined in xsl:key.
  51. */
  52. private QName _name;
  53. /**
  54. * The pattern to match starting at the root node.
  55. */
  56. private Pattern _match;
  57. /**
  58. * The expression that generates the values for this key.
  59. */
  60. private Expression _use;
  61. /**
  62. * The type of the _use expression.
  63. */
  64. private Type _useType;
  65. /**
  66. * Parse the <xsl:key> element and attributes
  67. * @param parser A reference to the stylesheet parser
  68. */
  69. public void parseContents(Parser parser) {
  70. // Get the required attributes and parser XPath expressions
  71. final String name = getAttribute("name");
  72. if (!XMLChar.isValidQName(name)){
  73. ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name, this);
  74. parser.reportError(Constants.ERROR, err);
  75. }
  76. _name = parser.getQNameIgnoreDefaultNs(name);
  77. _match = parser.parsePattern(this, "match", null);
  78. _use = parser.parseExpression(this, "use", null);
  79. // Make sure required attribute(s) have been set
  80. if (_name == null) {
  81. reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "name");
  82. return;
  83. }
  84. if (_match.isDummy()) {
  85. reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "match");
  86. return;
  87. }
  88. if (_use.isDummy()) {
  89. reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "use");
  90. return;
  91. }
  92. }
  93. /**
  94. * Returns a String-representation of this key's name
  95. * @return The key's name (from the <xsl:key> elements 'name' attribute).
  96. */
  97. public String getName() {
  98. return _name.toString();
  99. }
  100. public Type typeCheck(SymbolTable stable) throws TypeCheckError {
  101. // Type check match pattern
  102. _match.typeCheck(stable);
  103. // Cast node values to string values (except for nodesets)
  104. _useType = _use.typeCheck(stable);
  105. if (_useType instanceof StringType == false &&
  106. _useType instanceof NodeSetType == false)
  107. {
  108. _use = new CastExpr(_use, Type.String);
  109. }
  110. return Type.Void;
  111. }
  112. /**
  113. * This method is called if the "use" attribute of the key contains a
  114. * node set. In this case we must traverse all nodes in the set and
  115. * create one entry in this key's index for each node in the set.
  116. */
  117. public void traverseNodeSet(ClassGenerator classGen,
  118. MethodGenerator methodGen,
  119. int buildKeyIndex) {
  120. final ConstantPoolGen cpg = classGen.getConstantPool();
  121. final InstructionList il = methodGen.getInstructionList();
  122. // DOM.getStringValueX(nodeIndex) => String
  123. final int getNodeValue = cpg.addInterfaceMethodref(DOM_INTF,
  124. GET_NODE_VALUE,
  125. "(I)"+STRING_SIG);
  126. final int getNodeIdent = cpg.addInterfaceMethodref(DOM_INTF,
  127. "getNodeIdent",
  128. "(I)"+NODE_SIG);
  129. // AbstractTranslet.SetKeyIndexDom(name, Dom) => void
  130. final int keyDom = cpg.addMethodref(TRANSLET_CLASS,
  131. "setKeyIndexDom",
  132. "("+STRING_SIG+DOM_INTF_SIG+")V");
  133. // This variable holds the id of the node we found with the "match"
  134. // attribute of xsl:key. This is the id we store, with the value we
  135. // get from the nodes we find here, in the index for this key.
  136. final LocalVariableGen parentNode =
  137. methodGen.addLocalVariable("parentNode",
  138. Util.getJCRefType("I"),
  139. il.getEnd(), null);
  140. // Get the 'parameter' from the stack and store it in a local var.
  141. il.append(new ISTORE(parentNode.getIndex()));
  142. il.append(methodGen.loadDOM());
  143. il.append(new ILOAD(parentNode.getIndex()));
  144. il.append(new INVOKEINTERFACE(getNodeIdent, 2));
  145. il.append(new ISTORE(parentNode.getIndex()));
  146. // Save current node and current iterator on the stack
  147. il.append(methodGen.loadCurrentNode());
  148. il.append(methodGen.loadIterator());
  149. // Overwrite current iterator with one that gives us only what we want
  150. _use.translate(classGen, methodGen);
  151. _use.startIterator(classGen, methodGen);
  152. il.append(methodGen.storeIterator());
  153. final BranchHandle nextNode = il.append(new GOTO(null));
  154. final InstructionHandle loop = il.append(NOP);
  155. // Prepare to call buildKeyIndex(String name, int node, String value);
  156. il.append(classGen.loadTranslet());
  157. il.append(new PUSH(cpg, _name.toString()));
  158. il.append(new ILOAD(parentNode.getIndex()));
  159. // Now get the node value and feck it on the parameter stack
  160. il.append(methodGen.loadDOM());
  161. il.append(methodGen.loadCurrentNode());
  162. il.append(new INVOKEINTERFACE(getNodeValue, 2));
  163. // Finally do the call to add an entry in the index for this key.
  164. il.append(new INVOKEVIRTUAL(buildKeyIndex));
  165. il.append(classGen.loadTranslet());
  166. il.append(new PUSH(cpg, getName()));
  167. il.append(methodGen.loadDOM());
  168. il.append(new INVOKEVIRTUAL(keyDom));
  169. nextNode.setTarget(il.append(methodGen.loadIterator()));
  170. il.append(methodGen.nextNode());
  171. il.append(DUP);
  172. il.append(methodGen.storeCurrentNode());
  173. il.append(new IFGE(loop)); // Go on to next matching node....
  174. // Restore current node and current iterator from the stack
  175. il.append(methodGen.storeIterator());
  176. il.append(methodGen.storeCurrentNode());
  177. }
  178. /**
  179. * Gather all nodes that match the expression in the attribute "match"
  180. * and add one (or more) entries in this key's index.
  181. */
  182. public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
  183. final ConstantPoolGen cpg = classGen.getConstantPool();
  184. final InstructionList il = methodGen.getInstructionList();
  185. final int current = methodGen.getLocalIndex("current");
  186. // AbstractTranslet.buildKeyIndex(name,node_id,value) => void
  187. final int key = cpg.addMethodref(TRANSLET_CLASS,
  188. "buildKeyIndex",
  189. "("+STRING_SIG+"I"+OBJECT_SIG+")V");
  190. // AbstractTranslet.SetKeyIndexDom(name, Dom) => void
  191. final int keyDom = cpg.addMethodref(TRANSLET_CLASS,
  192. "setKeyIndexDom",
  193. "("+STRING_SIG+DOM_INTF_SIG+")V");
  194. final int getNodeIdent = cpg.addInterfaceMethodref(DOM_INTF,
  195. "getNodeIdent",
  196. "(I)"+NODE_SIG);
  197. // DOM.getAxisIterator(root) => NodeIterator
  198. final int git = cpg.addInterfaceMethodref(DOM_INTF,
  199. "getAxisIterator",
  200. "(I)"+NODE_ITERATOR_SIG);
  201. il.append(methodGen.loadCurrentNode());
  202. il.append(methodGen.loadIterator());
  203. // Get an iterator for all nodes in the DOM
  204. il.append(methodGen.loadDOM());
  205. il.append(new PUSH(cpg,Axis.DESCENDANT));
  206. il.append(new INVOKEINTERFACE(git, 2));
  207. // Reset the iterator to start with the root node
  208. il.append(methodGen.loadCurrentNode());
  209. il.append(methodGen.setStartNode());
  210. il.append(methodGen.storeIterator());
  211. // Loop for traversing all nodes in the DOM
  212. final BranchHandle nextNode = il.append(new GOTO(null));
  213. final InstructionHandle loop = il.append(NOP);
  214. // Check if the current node matches the pattern in "match"
  215. il.append(methodGen.loadCurrentNode());
  216. _match.translate(classGen, methodGen);
  217. _match.synthesize(classGen, methodGen); // Leaves 0 or 1 on stack
  218. final BranchHandle skipNode = il.append(new IFEQ(null));
  219. // If this is a node-set we must go through each node in the set
  220. if (_useType instanceof NodeSetType) {
  221. // Pass current node as parameter (we're indexing on that node)
  222. il.append(methodGen.loadCurrentNode());
  223. traverseNodeSet(classGen, methodGen, key);
  224. }
  225. else {
  226. il.append(classGen.loadTranslet());
  227. il.append(DUP);
  228. il.append(new PUSH(cpg, _name.toString()));
  229. il.append(DUP_X1);
  230. il.append(methodGen.loadCurrentNode());
  231. _use.translate(classGen, methodGen);
  232. il.append(SWAP);
  233. il.append(methodGen.loadDOM());
  234. il.append(SWAP);
  235. il.append(new INVOKEINTERFACE(getNodeIdent, 2));
  236. il.append(SWAP);
  237. il.append(new INVOKEVIRTUAL(key));
  238. il.append(methodGen.loadDOM());
  239. il.append(new INVOKEVIRTUAL(keyDom));
  240. }
  241. // Get the next node from the iterator and do loop again...
  242. final InstructionHandle skip = il.append(NOP);
  243. il.append(methodGen.loadIterator());
  244. il.append(methodGen.nextNode());
  245. il.append(DUP);
  246. il.append(methodGen.storeCurrentNode());
  247. il.append(new IFGT(loop));
  248. // Restore current node and current iterator from the stack
  249. il.append(methodGen.storeIterator());
  250. il.append(methodGen.storeCurrentNode());
  251. nextNode.setTarget(skip);
  252. skipNode.setTarget(skip);
  253. }
  254. }