001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.jxpath.ri.compiler; 018 019import org.apache.commons.jxpath.Pointer; 020import org.apache.commons.jxpath.ri.Compiler; 021import org.apache.commons.jxpath.ri.EvalContext; 022import org.apache.commons.jxpath.ri.QName; 023import org.apache.commons.jxpath.ri.axes.AncestorContext; 024import org.apache.commons.jxpath.ri.axes.AttributeContext; 025import org.apache.commons.jxpath.ri.axes.ChildContext; 026import org.apache.commons.jxpath.ri.axes.DescendantContext; 027import org.apache.commons.jxpath.ri.axes.InitialContext; 028import org.apache.commons.jxpath.ri.axes.NamespaceContext; 029import org.apache.commons.jxpath.ri.axes.ParentContext; 030import org.apache.commons.jxpath.ri.axes.PrecedingOrFollowingContext; 031import org.apache.commons.jxpath.ri.axes.PredicateContext; 032import org.apache.commons.jxpath.ri.axes.SelfContext; 033import org.apache.commons.jxpath.ri.axes.SimplePathInterpreter; 034import org.apache.commons.jxpath.ri.axes.UnionContext; 035import org.apache.commons.jxpath.ri.model.NodePointer; 036 037/** 038 * @author Dmitri Plotnikov 039 * @version $Revision: 679912 $ $Date: 2008-07-26 00:23:10 +0200 (Sa, 26 Jul 2008) $ 040 */ 041public abstract class Path extends Expression { 042 043 private Step[] steps; 044 private boolean basicKnown = false; 045 private boolean basic; 046 047 /** 048 * Create a new Path. 049 * @param steps that compose the Path 050 */ 051 public Path(Step[] steps) { 052 this.steps = steps; 053 } 054 055 /** 056 * Get the steps. 057 * @return Step[] 058 */ 059 public Step[] getSteps() { 060 return steps; 061 } 062 063 public boolean computeContextDependent() { 064 if (steps != null) { 065 for (int i = 0; i < steps.length; i++) { 066 if (steps[i].isContextDependent()) { 067 return true; 068 } 069 } 070 } 071 return false; 072 } 073 074 /** 075 * Recognizes paths formatted as <code>foo/bar[3]/baz[@name = 'biz']</code>. 076 * The evaluation of such "simple" paths is optimized and 077 * streamlined. 078 * @return <code>true</code> if this path is simple 079 */ 080 public synchronized boolean isSimplePath() { 081 if (!basicKnown) { 082 basicKnown = true; 083 basic = true; 084 Step[] steps = getSteps(); 085 for (int i = 0; i < steps.length; i++) { 086 if (!isSimpleStep(steps[i])) { 087 basic = false; 088 break; 089 } 090 } 091 } 092 return basic; 093 } 094 095 /** 096 * A Step is "simple" if it takes one of these forms: ".", "/foo", 097 * "@bar", "/foo[3]". If there are predicates, they should be 098 * context independent for the step to still be considered simple. 099 * @param step the step to check 100 * @return boolean 101 */ 102 protected boolean isSimpleStep(Step step) { 103 if (step.getAxis() == Compiler.AXIS_SELF) { 104 NodeTest nodeTest = step.getNodeTest(); 105 if (!(nodeTest instanceof NodeTypeTest)) { 106 return false; 107 } 108 int nodeType = ((NodeTypeTest) nodeTest).getNodeType(); 109 if (nodeType != Compiler.NODE_TYPE_NODE) { 110 return false; 111 } 112 return areBasicPredicates(step.getPredicates()); 113 } 114 if (step.getAxis() == Compiler.AXIS_CHILD 115 || step.getAxis() == Compiler.AXIS_ATTRIBUTE) { 116 NodeTest nodeTest = step.getNodeTest(); 117 if (!(nodeTest instanceof NodeNameTest)) { 118 return false; 119 } 120 if (((NodeNameTest) nodeTest).isWildcard()) { 121 return false; 122 } 123 return areBasicPredicates(step.getPredicates()); 124 } 125 return false; 126 } 127 128 /** 129 * Learn whether the elements of the specified array are "basic" predicates. 130 * @param predicates the Expression[] to check 131 * @return boolean 132 */ 133 protected boolean areBasicPredicates(Expression[] predicates) { 134 if (predicates != null && predicates.length != 0) { 135 boolean firstIndex = true; 136 for (int i = 0; i < predicates.length; i++) { 137 if (predicates[i] instanceof NameAttributeTest) { 138 if (((NameAttributeTest) predicates[i]) 139 .getNameTestExpression() 140 .isContextDependent()) { 141 return false; 142 } 143 } 144 else if (predicates[i].isContextDependent()) { 145 return false; 146 } 147 else { 148 if (!firstIndex) { 149 return false; 150 } 151 firstIndex = false; 152 } 153 } 154 } 155 return true; 156 } 157 158 /** 159 * Given a root context, walks a path therefrom and finds the 160 * pointer to the first element matching the path. 161 * @param context evaluation context 162 * @return Pointer 163 */ 164 protected Pointer getSingleNodePointerForSteps(EvalContext context) { 165 if (steps.length == 0) { 166 return context.getSingleNodePointer(); 167 } 168 169 if (isSimplePath()) { 170 NodePointer ptr = (NodePointer) context.getSingleNodePointer(); 171 return SimplePathInterpreter.interpretSimpleLocationPath( 172 context, 173 ptr, 174 steps); 175 } 176 return searchForPath(context); 177 } 178 179 /** 180 * The idea here is to return a NullPointer rather than null if that's at 181 * all possible. Take for example this path: "//map/key". Let's say, "map" 182 * is an existing node, but "key" is not there. We will create a 183 * NullPointer that can be used to set/create the "key" property. 184 * <p> 185 * However, a path like "//key" would still produce null, because we have 186 * no way of knowing where "key" would be if it existed. 187 * </p> 188 * <p> 189 * To accomplish this, we first try the path itself. If it does not find 190 * anything, we chop off last step of the path, as long as it is a simple 191 * one like child:: or attribute:: and try to evaluate the truncated path. 192 * If it finds exactly one node - create a NullPointer and return. If it 193 * fails, chop off another step and repeat. If it finds more than one 194 * location - return null. 195 * </p> 196 * @param context evaluation context 197 * @return Pointer 198 */ 199 protected Pointer searchForPath(EvalContext context) { 200 EvalContext ctx = buildContextChain(context, steps.length, true); 201 Pointer pointer = ctx.getSingleNodePointer(); 202 203 if (pointer != null) { 204 return pointer; 205 } 206 207 for (int i = steps.length; --i > 0;) { 208 if (!isSimpleStep(steps[i])) { 209 return null; 210 } 211 ctx = buildContextChain(context, i, true); 212 if (ctx.hasNext()) { 213 Pointer partial = (Pointer) ctx.next(); 214 if (ctx.hasNext()) { 215 // If we find another location - the search is 216 // ambiguous, so we report failure 217 return null; 218 } 219 if (partial instanceof NodePointer) { 220 return SimplePathInterpreter.createNullPointer( 221 context, 222 (NodePointer) partial, 223 steps, 224 i); 225 } 226 } 227 } 228 return null; 229 } 230 231 /** 232 * Given a root context, walks a path therefrom and builds a context 233 * that contains all nodes matching the path. 234 * @param context evaluation context 235 * @return EvaluationContext 236 */ 237 protected EvalContext evalSteps(EvalContext context) { 238 return buildContextChain(context, steps.length, false); 239 } 240 241 /** 242 * Build a context from a chain of contexts. 243 * @param context evaluation context 244 * @param stepCount number of steps to descend 245 * @param createInitialContext whether to create the initial context 246 * @return created context 247 */ 248 protected EvalContext buildContextChain( 249 EvalContext context, 250 int stepCount, 251 boolean createInitialContext) { 252 if (createInitialContext) { 253 context = new InitialContext(context); 254 } 255 if (steps.length == 0) { 256 return context; 257 } 258 for (int i = 0; i < stepCount; i++) { 259 context = 260 createContextForStep( 261 context, 262 steps[i].getAxis(), 263 steps[i].getNodeTest()); 264 Expression[] predicates = steps[i].getPredicates(); 265 if (predicates != null) { 266 for (int j = 0; j < predicates.length; j++) { 267 if (j != 0) { 268 context = new UnionContext(context, new EvalContext[]{context}); 269 } 270 context = new PredicateContext(context, predicates[j]); 271 } 272 } 273 } 274 return context; 275 } 276 277 /** 278 * Different axes are serviced by different contexts. This method 279 * allocates the right context for the supplied step. 280 * @param context evaluation context 281 * @param axis code 282 * @param nodeTest node test 283 * @return EvalContext 284 */ 285 protected EvalContext createContextForStep( 286 EvalContext context, 287 int axis, 288 NodeTest nodeTest) { 289 if (nodeTest instanceof NodeNameTest) { 290 QName qname = ((NodeNameTest) nodeTest).getNodeName(); 291 String prefix = qname.getPrefix(); 292 if (prefix != null) { 293 String namespaceURI = context.getJXPathContext() 294 .getNamespaceURI(prefix); 295 nodeTest = new NodeNameTest(qname, namespaceURI); 296 } 297 } 298 299 switch (axis) { 300 case Compiler.AXIS_ANCESTOR : 301 return new AncestorContext(context, false, nodeTest); 302 case Compiler.AXIS_ANCESTOR_OR_SELF : 303 return new AncestorContext(context, true, nodeTest); 304 case Compiler.AXIS_ATTRIBUTE : 305 return new AttributeContext(context, nodeTest); 306 case Compiler.AXIS_CHILD : 307 return new ChildContext(context, nodeTest, false, false); 308 case Compiler.AXIS_DESCENDANT : 309 return new DescendantContext(context, false, nodeTest); 310 case Compiler.AXIS_DESCENDANT_OR_SELF : 311 return new DescendantContext(context, true, nodeTest); 312 case Compiler.AXIS_FOLLOWING : 313 return new PrecedingOrFollowingContext(context, nodeTest, false); 314 case Compiler.AXIS_FOLLOWING_SIBLING : 315 return new ChildContext(context, nodeTest, true, false); 316 case Compiler.AXIS_NAMESPACE : 317 return new NamespaceContext(context, nodeTest); 318 case Compiler.AXIS_PARENT : 319 return new ParentContext(context, nodeTest); 320 case Compiler.AXIS_PRECEDING : 321 return new PrecedingOrFollowingContext(context, nodeTest, true); 322 case Compiler.AXIS_PRECEDING_SIBLING : 323 return new ChildContext(context, nodeTest, true, true); 324 case Compiler.AXIS_SELF : 325 return new SelfContext(context, nodeTest); 326 default: 327 return null; // Never happens 328 } 329 } 330}