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. package org.apache.commons.jxpath.ri.compiler;
  17. import org.apache.commons.jxpath.Pointer;
  18. import org.apache.commons.jxpath.ri.Compiler;
  19. import org.apache.commons.jxpath.ri.EvalContext;
  20. import org.apache.commons.jxpath.ri.QName;
  21. import org.apache.commons.jxpath.ri.axes.AncestorContext;
  22. import org.apache.commons.jxpath.ri.axes.AttributeContext;
  23. import org.apache.commons.jxpath.ri.axes.ChildContext;
  24. import org.apache.commons.jxpath.ri.axes.DescendantContext;
  25. import org.apache.commons.jxpath.ri.axes.InitialContext;
  26. import org.apache.commons.jxpath.ri.axes.NamespaceContext;
  27. import org.apache.commons.jxpath.ri.axes.ParentContext;
  28. import org.apache.commons.jxpath.ri.axes.PrecedingOrFollowingContext;
  29. import org.apache.commons.jxpath.ri.axes.PredicateContext;
  30. import org.apache.commons.jxpath.ri.axes.SelfContext;
  31. import org.apache.commons.jxpath.ri.axes.SimplePathInterpreter;
  32. import org.apache.commons.jxpath.ri.model.NodePointer;
  33. /**
  34. * @author Dmitri Plotnikov
  35. * @version $Revision: 1.14 $ $Date: 2004/04/01 02:55:32 $
  36. */
  37. public abstract class Path extends Expression {
  38. private Step[] steps;
  39. private boolean basicKnown = false;
  40. private boolean basic;
  41. public Path(Step[] steps) {
  42. this.steps = steps;
  43. }
  44. public Step[] getSteps() {
  45. return steps;
  46. }
  47. public boolean computeContextDependent() {
  48. if (steps != null) {
  49. for (int i = 0; i < steps.length; i++) {
  50. if (steps[i].isContextDependent()) {
  51. return true;
  52. }
  53. }
  54. }
  55. return false;
  56. }
  57. /**
  58. * Recognized paths formatted as <code>foo/bar[3]/baz[@name = 'biz']
  59. * </code>. The evaluation of such "simple" paths is optimized and
  60. * streamlined.
  61. */
  62. public boolean isSimplePath() {
  63. if (!basicKnown) {
  64. basicKnown = true;
  65. basic = true;
  66. Step[] steps = getSteps();
  67. for (int i = 0; i < steps.length; i++) {
  68. if (!isSimpleStep(steps[i])){
  69. basic = false;
  70. break;
  71. }
  72. }
  73. }
  74. return basic;
  75. }
  76. /**
  77. * A Step is "simple" if it takes one of these forms: ".", "/foo",
  78. * "@bar", "/foo[3]". If there are predicates, they should be
  79. * context independent for the step to still be considered simple.
  80. */
  81. protected boolean isSimpleStep(Step step) {
  82. if (step.getAxis() == Compiler.AXIS_SELF) {
  83. NodeTest nodeTest = step.getNodeTest();
  84. if (!(nodeTest instanceof NodeTypeTest)) {
  85. return false;
  86. }
  87. int nodeType = ((NodeTypeTest) nodeTest).getNodeType();
  88. if (nodeType != Compiler.NODE_TYPE_NODE) {
  89. return false;
  90. }
  91. return areBasicPredicates(step.getPredicates());
  92. }
  93. else if (step.getAxis() == Compiler.AXIS_CHILD
  94. || step.getAxis() == Compiler.AXIS_ATTRIBUTE) {
  95. NodeTest nodeTest = step.getNodeTest();
  96. if (!(nodeTest instanceof NodeNameTest)){
  97. return false;
  98. }
  99. if (((NodeNameTest) nodeTest).isWildcard()) {
  100. return false;
  101. }
  102. return areBasicPredicates(step.getPredicates());
  103. }
  104. return false;
  105. }
  106. protected boolean areBasicPredicates(Expression predicates[]) {
  107. if (predicates != null && predicates.length != 0) {
  108. boolean firstIndex = true;
  109. for (int i = 0; i < predicates.length; i++) {
  110. if (predicates[i] instanceof NameAttributeTest) {
  111. if (((NameAttributeTest) predicates[i])
  112. .getNameTestExpression()
  113. .isContextDependent()) {
  114. return false;
  115. }
  116. }
  117. else if (predicates[i].isContextDependent()) {
  118. return false;
  119. }
  120. else {
  121. if (!firstIndex) {
  122. return false;
  123. }
  124. firstIndex = false;
  125. }
  126. }
  127. }
  128. return true;
  129. }
  130. /**
  131. * Given a root context, walks a path therefrom and finds the
  132. * pointer to the first element matching the path.
  133. */
  134. protected Pointer getSingleNodePointerForSteps(EvalContext context) {
  135. if (steps.length == 0) {
  136. return context.getSingleNodePointer();
  137. }
  138. if (isSimplePath()) {
  139. NodePointer ptr = (NodePointer) context.getSingleNodePointer();
  140. return SimplePathInterpreter.interpretSimpleLocationPath(
  141. context,
  142. ptr,
  143. steps);
  144. }
  145. else {
  146. return searchForPath(context);
  147. }
  148. }
  149. /**
  150. * The idea here is to return a NullPointer rather than null if that's at
  151. * all possible. Take for example this path: "//map/key". Let's say, "map"
  152. * is an existing node, but "key" is not there. We will create a
  153. * NullPointer that can be used to set/create the "key" property.
  154. * <p>
  155. * However, a path like "//key" would still produce null, because we have
  156. * no way of knowing where "key" would be if it existed.
  157. * </p>
  158. * <p>
  159. * To accomplish this, we first try the path itself. If it does not find
  160. * anything, we chop off last step of the path, as long as it is a simple
  161. * one like child:: or attribute:: and try to evaluate the truncated path.
  162. * If it finds exactly one node - create a NullPointer and return. If it
  163. * fails, chop off another step and repeat. If it finds more than one
  164. * location - return null.
  165. * </p>
  166. */
  167. private Pointer searchForPath(EvalContext context) {
  168. EvalContext ctx = buildContextChain(context, steps.length, true);
  169. Pointer pointer = ctx.getSingleNodePointer();
  170. if (pointer != null) {
  171. return pointer;
  172. }
  173. for (int i = steps.length; --i > 0;) {
  174. if (!isSimpleStep(steps[i])) {
  175. return null;
  176. }
  177. ctx = buildContextChain(context, i, true);
  178. if (ctx.hasNext()) {
  179. Pointer partial = (Pointer) ctx.next();
  180. if (ctx.hasNext()) {
  181. // If we find another location - the search is
  182. // ambiguous, so we report failure
  183. return null;
  184. }
  185. if (partial instanceof NodePointer) {
  186. return SimplePathInterpreter.createNullPointer(
  187. context,
  188. (NodePointer) partial,
  189. steps,
  190. i);
  191. }
  192. }
  193. }
  194. return null;
  195. }
  196. /**
  197. * Given a root context, walks a path therefrom and builds a context
  198. * that contains all nodes matching the path.
  199. */
  200. protected EvalContext evalSteps(EvalContext context) {
  201. return buildContextChain(context, steps.length, false);
  202. }
  203. private EvalContext buildContextChain(
  204. EvalContext context,
  205. int stepCount,
  206. boolean createInitialContext)
  207. {
  208. if (createInitialContext) {
  209. context = new InitialContext(context);
  210. }
  211. if (steps.length == 0) {
  212. return context;
  213. }
  214. for (int i = 0; i < stepCount; i++) {
  215. context =
  216. createContextForStep(
  217. context,
  218. steps[i].getAxis(),
  219. steps[i].getNodeTest());
  220. Expression predicates[] = steps[i].getPredicates();
  221. if (predicates != null) {
  222. for (int j = 0; j < predicates.length; j++) {
  223. context = new PredicateContext(context, predicates[j]);
  224. }
  225. }
  226. }
  227. return context;
  228. }
  229. /**
  230. * Different axes are serviced by different contexts. This method
  231. * allocates the right context for the supplied step.
  232. */
  233. protected EvalContext createContextForStep(
  234. EvalContext context,
  235. int axis,
  236. NodeTest nodeTest)
  237. {
  238. if (nodeTest instanceof NodeNameTest) {
  239. QName qname = ((NodeNameTest) nodeTest).getNodeName();
  240. String prefix = qname.getPrefix();
  241. if (prefix != null) {
  242. String namespaceURI = context.getJXPathContext()
  243. .getNamespaceURI(prefix);
  244. nodeTest = new NodeNameTest(qname, namespaceURI);
  245. }
  246. }
  247. switch (axis) {
  248. case Compiler.AXIS_ANCESTOR :
  249. return new AncestorContext(context, false, nodeTest);
  250. case Compiler.AXIS_ANCESTOR_OR_SELF :
  251. return new AncestorContext(context, true, nodeTest);
  252. case Compiler.AXIS_ATTRIBUTE :
  253. return new AttributeContext(context, nodeTest);
  254. case Compiler.AXIS_CHILD :
  255. return new ChildContext(context, nodeTest, false, false);
  256. case Compiler.AXIS_DESCENDANT :
  257. return new DescendantContext(context, false, nodeTest);
  258. case Compiler.AXIS_DESCENDANT_OR_SELF :
  259. return new DescendantContext(context, true, nodeTest);
  260. case Compiler.AXIS_FOLLOWING :
  261. return new PrecedingOrFollowingContext(context, nodeTest, false);
  262. case Compiler.AXIS_FOLLOWING_SIBLING :
  263. return new ChildContext(context, nodeTest, true, false);
  264. case Compiler.AXIS_NAMESPACE :
  265. return new NamespaceContext(context, nodeTest);
  266. case Compiler.AXIS_PARENT :
  267. return new ParentContext(context, nodeTest);
  268. case Compiler.AXIS_PRECEDING :
  269. return new PrecedingOrFollowingContext(context, nodeTest, true);
  270. case Compiler.AXIS_PRECEDING_SIBLING :
  271. return new ChildContext(context, nodeTest, true, true);
  272. case Compiler.AXIS_SELF :
  273. return new SelfContext(context, nodeTest);
  274. }
  275. return null; // Never happens
  276. }
  277. }