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: Step.java,v 1.46 2004/02/24 04:21:22 zongaro Exp $
  18. */
  19. package com.sun.org.apache.xalan.internal.xsltc.compiler;
  20. import java.util.Vector;
  21. import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
  22. import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  23. import com.sun.org.apache.bcel.internal.generic.ICONST;
  24. import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  25. import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  26. import com.sun.org.apache.bcel.internal.generic.InstructionList;
  27. import com.sun.org.apache.bcel.internal.generic.NEW;
  28. import com.sun.org.apache.bcel.internal.generic.PUSH;
  29. import com.sun.org.apache.xalan.internal.xsltc.DOM;
  30. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  31. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  32. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  33. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  34. import com.sun.org.apache.xalan.internal.xsltc.dom.Axis;
  35. import com.sun.org.apache.xml.internal.dtm.DTM;
  36. /**
  37. * @author Jacek Ambroziak
  38. * @author Santiago Pericas-Geertsen
  39. * @author Morten Jorgensen
  40. */
  41. final class Step extends RelativeLocationPath {
  42. /**
  43. * This step's axis as defined in class Axis.
  44. */
  45. private int _axis;
  46. /**
  47. * A vector of predicates (filters) defined on this step - may be null
  48. */
  49. private Vector _predicates;
  50. /**
  51. * Some simple predicates can be handled by this class (and not by the
  52. * Predicate class) and will be removed from the above vector as they are
  53. * handled. We use this boolean to remember if we did have any predicates.
  54. */
  55. private boolean _hadPredicates = false;
  56. /**
  57. * Type of the node test.
  58. */
  59. private int _nodeType;
  60. public Step(int axis, int nodeType, Vector predicates) {
  61. _axis = axis;
  62. _nodeType = nodeType;
  63. _predicates = predicates;
  64. }
  65. /**
  66. * Set the parser for this element and all child predicates
  67. */
  68. public void setParser(Parser parser) {
  69. super.setParser(parser);
  70. if (_predicates != null) {
  71. final int n = _predicates.size();
  72. for (int i = 0; i < n; i++) {
  73. final Predicate exp = (Predicate)_predicates.elementAt(i);
  74. exp.setParser(parser);
  75. exp.setParent(this);
  76. }
  77. }
  78. }
  79. /**
  80. * Define the axis (defined in Axis class) for this step
  81. */
  82. public int getAxis() {
  83. return _axis;
  84. }
  85. /**
  86. * Get the axis (defined in Axis class) for this step
  87. */
  88. public void setAxis(int axis) {
  89. _axis = axis;
  90. }
  91. /**
  92. * Returns the node-type for this step
  93. */
  94. public int getNodeType() {
  95. return _nodeType;
  96. }
  97. /**
  98. * Returns the vector containing all predicates for this step.
  99. */
  100. public Vector getPredicates() {
  101. return _predicates;
  102. }
  103. /**
  104. * Returns the vector containing all predicates for this step.
  105. */
  106. public void addPredicates(Vector predicates) {
  107. if (_predicates == null) {
  108. _predicates = predicates;
  109. }
  110. else {
  111. _predicates.addAll(predicates);
  112. }
  113. }
  114. /**
  115. * Returns 'true' if this step has a parent pattern.
  116. * This method will return 'false' if this step occurs on its own under
  117. * an element like <xsl:for-each> or <xsl:apply-templates>.
  118. */
  119. private boolean hasParentPattern() {
  120. final SyntaxTreeNode parent = getParent();
  121. return (parent instanceof ParentPattern ||
  122. parent instanceof ParentLocationPath ||
  123. parent instanceof UnionPathExpr ||
  124. parent instanceof FilterParentPath);
  125. }
  126. /**
  127. * Returns 'true' if this step has any predicates
  128. */
  129. private boolean hasPredicates() {
  130. return _predicates != null && _predicates.size() > 0;
  131. }
  132. /**
  133. * Returns 'true' if this step is used within a predicate
  134. */
  135. private boolean isPredicate() {
  136. SyntaxTreeNode parent = this;
  137. while (parent != null) {
  138. parent = parent.getParent();
  139. if (parent instanceof Predicate) return true;
  140. }
  141. return false;
  142. }
  143. /**
  144. * True if this step is the abbreviated step '.'
  145. */
  146. public boolean isAbbreviatedDot() {
  147. return _nodeType == NodeTest.ANODE && _axis == Axis.SELF;
  148. }
  149. /**
  150. * True if this step is the abbreviated step '..'
  151. */
  152. public boolean isAbbreviatedDDot() {
  153. return _nodeType == NodeTest.ANODE && _axis == Axis.PARENT;
  154. }
  155. /**
  156. * Type check this step. The abbreviated steps '.' and '@attr' are
  157. * assigned type node if they have no predicates. All other steps
  158. * have type node-set.
  159. */
  160. public Type typeCheck(SymbolTable stable) throws TypeCheckError {
  161. // Save this value for later - important for testing for special
  162. // combinations of steps and patterns than can be optimised
  163. _hadPredicates = hasPredicates();
  164. // Special case for '.'
  165. // in the case where '.' has a context such as book/.
  166. // or .[false()] we can not optimize the nodeset to a single node.
  167. if (isAbbreviatedDot()) {
  168. _type = (hasParentPattern() || hasPredicates() ) ?
  169. Type.NodeSet : Type.Node;
  170. }
  171. else {
  172. _type = Type.NodeSet;
  173. }
  174. // Type check all predicates (expressions applied to the step)
  175. if (_predicates != null) {
  176. final int n = _predicates.size();
  177. for (int i = 0; i < n; i++) {
  178. final Expression pred = (Expression)_predicates.elementAt(i);
  179. pred.typeCheck(stable);
  180. }
  181. }
  182. // Return either Type.Node or Type.NodeSet
  183. return _type;
  184. }
  185. /**
  186. * Translate a step by pushing the appropriate iterator onto the stack.
  187. * The abbreviated steps '.' and '@attr' do not create new iterators
  188. * if they are not part of a LocationPath and have no filters.
  189. * In these cases a node index instead of an iterator is pushed
  190. * onto the stack.
  191. */
  192. public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
  193. final ConstantPoolGen cpg = classGen.getConstantPool();
  194. final InstructionList il = methodGen.getInstructionList();
  195. if (hasPredicates()) {
  196. translatePredicates(classGen, methodGen);
  197. } else {
  198. int star = 0;
  199. String name = null;
  200. final XSLTC xsltc = getParser().getXSLTC();
  201. if (_nodeType >= DTM.NTYPES) {
  202. final Vector ni = xsltc.getNamesIndex();
  203. name = (String)ni.elementAt(_nodeType-DTM.NTYPES);
  204. star = name.lastIndexOf('*');
  205. }
  206. // If it is an attribute, but not '@*', '@pre:*' or '@node()',
  207. // and has no parent
  208. if (_axis == Axis.ATTRIBUTE && _nodeType != NodeTest.ATTRIBUTE
  209. && _nodeType != NodeTest.ANODE && !hasParentPattern()
  210. && star == 0)
  211. {
  212. int iter = cpg.addInterfaceMethodref(DOM_INTF,
  213. "getTypedAxisIterator",
  214. "(II)"+NODE_ITERATOR_SIG);
  215. il.append(methodGen.loadDOM());
  216. il.append(new PUSH(cpg, Axis.ATTRIBUTE));
  217. il.append(new PUSH(cpg, _nodeType));
  218. il.append(new INVOKEINTERFACE(iter, 3));
  219. return;
  220. }
  221. // Special case for '.'
  222. if (isAbbreviatedDot()) {
  223. if (_type == Type.Node) {
  224. // Put context node on stack if using Type.Node
  225. il.append(methodGen.loadContextNode());
  226. }
  227. else {
  228. // Wrap the context node in a singleton iterator if not.
  229. int init = cpg.addMethodref(SINGLETON_ITERATOR,
  230. "<init>", "("+NODE_SIG+")V");
  231. il.append(new NEW(cpg.addClass(SINGLETON_ITERATOR)));
  232. il.append(DUP);
  233. il.append(methodGen.loadContextNode());
  234. il.append(new INVOKESPECIAL(init));
  235. }
  236. return;
  237. }
  238. // Special case for /foo/*/bar
  239. SyntaxTreeNode parent = getParent();
  240. if ((parent instanceof ParentLocationPath) &&
  241. (parent.getParent() instanceof ParentLocationPath)) {
  242. if ((_nodeType == NodeTest.ELEMENT) && (!_hadPredicates)) {
  243. _nodeType = NodeTest.ANODE;
  244. }
  245. }
  246. // "ELEMENT" or "*" or "@*" or ".." or "@attr" with a parent.
  247. switch (_nodeType) {
  248. case NodeTest.ATTRIBUTE:
  249. _axis = Axis.ATTRIBUTE;
  250. case NodeTest.ANODE:
  251. // DOM.getAxisIterator(int axis);
  252. int git = cpg.addInterfaceMethodref(DOM_INTF,
  253. "getAxisIterator",
  254. "(I)"+NODE_ITERATOR_SIG);
  255. il.append(methodGen.loadDOM());
  256. il.append(new PUSH(cpg, _axis));
  257. il.append(new INVOKEINTERFACE(git, 2));
  258. break;
  259. default:
  260. if (star > 1) {
  261. final String namespace;
  262. if (_axis == Axis.ATTRIBUTE)
  263. namespace = name.substring(0,star-2);
  264. else
  265. namespace = name.substring(0,star-1);
  266. final int nsType = xsltc.registerNamespace(namespace);
  267. final int ns = cpg.addInterfaceMethodref(DOM_INTF,
  268. "getNamespaceAxisIterator",
  269. "(II)"+NODE_ITERATOR_SIG);
  270. il.append(methodGen.loadDOM());
  271. il.append(new PUSH(cpg, _axis));
  272. il.append(new PUSH(cpg, nsType));
  273. il.append(new INVOKEINTERFACE(ns, 3));
  274. break;
  275. }
  276. case NodeTest.ELEMENT:
  277. // DOM.getTypedAxisIterator(int axis, int type);
  278. final int ty = cpg.addInterfaceMethodref(DOM_INTF,
  279. "getTypedAxisIterator",
  280. "(II)"+NODE_ITERATOR_SIG);
  281. // Get the typed iterator we're after
  282. il.append(methodGen.loadDOM());
  283. il.append(new PUSH(cpg, _axis));
  284. il.append(new PUSH(cpg, _nodeType));
  285. il.append(new INVOKEINTERFACE(ty, 3));
  286. break;
  287. }
  288. }
  289. }
  290. /**
  291. * Translate a sequence of predicates. Each predicate is translated
  292. * by constructing an instance of <code>CurrentNodeListIterator</code>
  293. * which is initialized from another iterator (recursive call),
  294. * a filter and a closure (call to translate on the predicate) and "this".
  295. */
  296. public void translatePredicates(ClassGenerator classGen,
  297. MethodGenerator methodGen) {
  298. final ConstantPoolGen cpg = classGen.getConstantPool();
  299. final InstructionList il = methodGen.getInstructionList();
  300. int idx = 0;
  301. if (_predicates.size() == 0) {
  302. translate(classGen, methodGen);
  303. }
  304. else {
  305. final Predicate predicate = (Predicate)_predicates.lastElement();
  306. _predicates.remove(predicate);
  307. // Special case for predicates that can use the NodeValueIterator
  308. // instead of an auxiliary class. Certain path/predicates pairs
  309. // are translated into a base path, on top of which we place a
  310. // node value iterator that tests for the desired value:
  311. // foo[@attr = 'str'] -> foo/@attr + test(value='str')
  312. // foo[bar = 'str'] -> foo/bar + test(value='str')
  313. // foo/bar[. = 'str'] -> foo/bar + test(value='str')
  314. if (predicate.isNodeValueTest()) {
  315. Step step = predicate.getStep();
  316. il.append(methodGen.loadDOM());
  317. // If the predicate's Step is simply '.' we translate this Step
  318. // and place the node test on top of the resulting iterator
  319. if (step.isAbbreviatedDot()) {
  320. translate(classGen, methodGen);
  321. il.append(new ICONST(DOM.RETURN_CURRENT));
  322. }
  323. // Otherwise we create a parent location path with this Step and
  324. // the predicates Step, and place the node test on top of that
  325. else {
  326. ParentLocationPath path = new ParentLocationPath(this,step);
  327. try {
  328. path.typeCheck(getParser().getSymbolTable());
  329. }
  330. catch (TypeCheckError e) { }
  331. path.translate(classGen, methodGen);
  332. il.append(new ICONST(DOM.RETURN_PARENT));
  333. }
  334. predicate.translate(classGen, methodGen);
  335. idx = cpg.addInterfaceMethodref(DOM_INTF,
  336. GET_NODE_VALUE_ITERATOR,
  337. GET_NODE_VALUE_ITERATOR_SIG);
  338. il.append(new INVOKEINTERFACE(idx, 5));
  339. }
  340. // Handle '//*[n]' expression
  341. else if (predicate.isNthDescendant()) {
  342. il.append(methodGen.loadDOM());
  343. // il.append(new ICONST(NodeTest.ELEMENT));
  344. il.append(new ICONST(predicate.getPosType()));
  345. predicate.translate(classGen, methodGen);
  346. il.append(new ICONST(0));
  347. idx = cpg.addInterfaceMethodref(DOM_INTF,
  348. "getNthDescendant",
  349. "(IIZ)"+NODE_ITERATOR_SIG);
  350. il.append(new INVOKEINTERFACE(idx, 4));
  351. }
  352. // Handle 'elem[n]' expression
  353. else if (predicate.isNthPositionFilter()) {
  354. idx = cpg.addMethodref(NTH_ITERATOR_CLASS,
  355. "<init>",
  356. "("+NODE_ITERATOR_SIG+"I)V");
  357. il.append(new NEW(cpg.addClass(NTH_ITERATOR_CLASS)));
  358. il.append(DUP);
  359. translatePredicates(classGen, methodGen); // recursive call
  360. predicate.translate(classGen, methodGen);
  361. il.append(new INVOKESPECIAL(idx));
  362. }
  363. else {
  364. idx = cpg.addMethodref(CURRENT_NODE_LIST_ITERATOR,
  365. "<init>",
  366. "("
  367. + NODE_ITERATOR_SIG
  368. + CURRENT_NODE_LIST_FILTER_SIG
  369. + NODE_SIG
  370. + TRANSLET_SIG
  371. + ")V");
  372. // create new CurrentNodeListIterator
  373. il.append(new NEW(cpg.addClass(CURRENT_NODE_LIST_ITERATOR)));
  374. il.append(DUP);
  375. translatePredicates(classGen, methodGen); // recursive call
  376. predicate.translateFilter(classGen, methodGen);
  377. il.append(methodGen.loadCurrentNode());
  378. il.append(classGen.loadTranslet());
  379. if (classGen.isExternal()) {
  380. final String className = classGen.getClassName();
  381. il.append(new CHECKCAST(cpg.addClass(className)));
  382. }
  383. il.append(new INVOKESPECIAL(idx));
  384. }
  385. }
  386. }
  387. /**
  388. * Returns a string representation of this step.
  389. */
  390. public String toString() {
  391. final StringBuffer buffer = new StringBuffer("step(\"");
  392. buffer.append(Axis.names[_axis]).append("\", ").append(_nodeType);
  393. if (_predicates != null) {
  394. final int n = _predicates.size();
  395. for (int i = 0; i < n; i++) {
  396. final Predicate pred = (Predicate)_predicates.elementAt(i);
  397. buffer.append(", ").append(pred.toString());
  398. }
  399. }
  400. return buffer.append(')').toString();
  401. }
  402. }