View Javadoc

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