001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: StepPattern.java 468655 2006-10-28 07:12:06Z minchau $
020 */
021 package org.apache.xpath.patterns;
022
023 import org.apache.xml.dtm.Axis;
024 import org.apache.xml.dtm.DTM;
025 import org.apache.xml.dtm.DTMAxisTraverser;
026 import org.apache.xml.dtm.DTMFilter;
027 import org.apache.xpath.Expression;
028 import org.apache.xpath.ExpressionOwner;
029 import org.apache.xpath.XPathContext;
030 import org.apache.xpath.XPathVisitor;
031 import org.apache.xpath.axes.SubContextList;
032 import org.apache.xpath.compiler.PsuedoNames;
033 import org.apache.xpath.objects.XObject;
034
035 /**
036 * This class represents a single pattern match step.
037 * @xsl.usage advanced
038 */
039 public class StepPattern extends NodeTest implements SubContextList, ExpressionOwner
040 {
041 static final long serialVersionUID = 9071668960168152644L;
042
043 /** The axis for this test. */
044 protected int m_axis;
045
046 /**
047 * Construct a StepPattern that tests for namespaces and node names.
048 *
049 *
050 * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
051 * @param namespace The namespace to be tested.
052 * @param name The local name to be tested.
053 * @param axis The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
054 * @param axisForPredicate No longer used.
055 */
056 public StepPattern(int whatToShow, String namespace, String name, int axis,
057 int axisForPredicate)
058 {
059
060 super(whatToShow, namespace, name);
061
062 m_axis = axis;
063 }
064
065 /**
066 * Construct a StepPattern that doesn't test for node names.
067 *
068 *
069 * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
070 * @param axis The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
071 * @param axisForPredicate No longer used.
072 */
073 public StepPattern(int whatToShow, int axis, int axisForPredicate)
074 {
075
076 super(whatToShow);
077
078 m_axis = axis;
079 }
080
081 /**
082 * The target local name or psuedo name, for hash table lookup optimization.
083 * @serial
084 */
085 String m_targetString; // only calculate on head
086
087 /**
088 * Calculate the local name or psuedo name of the node that this pattern will test,
089 * for hash table lookup optimization.
090 *
091 * @see org.apache.xpath.compiler.PsuedoNames
092 */
093 public void calcTargetString()
094 {
095
096 int whatToShow = getWhatToShow();
097
098 switch (whatToShow)
099 {
100 case DTMFilter.SHOW_COMMENT :
101 m_targetString = PsuedoNames.PSEUDONAME_COMMENT;
102 break;
103 case DTMFilter.SHOW_TEXT :
104 case DTMFilter.SHOW_CDATA_SECTION :
105 case (DTMFilter.SHOW_TEXT | DTMFilter.SHOW_CDATA_SECTION) :
106 m_targetString = PsuedoNames.PSEUDONAME_TEXT;
107 break;
108 case DTMFilter.SHOW_ALL :
109 m_targetString = PsuedoNames.PSEUDONAME_ANY;
110 break;
111 case DTMFilter.SHOW_DOCUMENT :
112 case DTMFilter.SHOW_DOCUMENT | DTMFilter.SHOW_DOCUMENT_FRAGMENT :
113 m_targetString = PsuedoNames.PSEUDONAME_ROOT;
114 break;
115 case DTMFilter.SHOW_ELEMENT :
116 if (this.WILD == m_name)
117 m_targetString = PsuedoNames.PSEUDONAME_ANY;
118 else
119 m_targetString = m_name;
120 break;
121 default :
122 m_targetString = PsuedoNames.PSEUDONAME_ANY;
123 break;
124 }
125 }
126
127 /**
128 * Get the local name or psuedo name of the node that this pattern will test,
129 * for hash table lookup optimization.
130 *
131 *
132 * @return local name or psuedo name of the node.
133 * @see org.apache.xpath.compiler.PsuedoNames
134 */
135 public String getTargetString()
136 {
137 return m_targetString;
138 }
139
140 /**
141 * Reference to nodetest and predicate for
142 * parent or ancestor.
143 * @serial
144 */
145 StepPattern m_relativePathPattern;
146
147 /**
148 * This function is used to fixup variables from QNames to stack frame
149 * indexes at stylesheet build time.
150 * @param vars List of QNames that correspond to variables. This list
151 * should be searched backwards for the first qualified name that
152 * corresponds to the variable reference qname. The position of the
153 * QName in the vector from the start of the vector will be its position
154 * in the stack frame (but variables above the globalsTop value will need
155 * to be offset to the current stack frame).
156 * @param globalsSize The number of variables in the global variable area.
157 */
158 public void fixupVariables(java.util.Vector vars, int globalsSize)
159 {
160
161 super.fixupVariables(vars, globalsSize);
162
163 if (null != m_predicates)
164 {
165 for (int i = 0; i < m_predicates.length; i++)
166 {
167 m_predicates[i].fixupVariables(vars, globalsSize);
168 }
169 }
170
171 if (null != m_relativePathPattern)
172 {
173 m_relativePathPattern.fixupVariables(vars, globalsSize);
174 }
175 }
176
177 /**
178 * Set the reference to nodetest and predicate for
179 * parent or ancestor.
180 *
181 *
182 * @param expr The relative pattern expression.
183 */
184 public void setRelativePathPattern(StepPattern expr)
185 {
186
187 m_relativePathPattern = expr;
188 expr.exprSetParent(this);
189
190 calcScore();
191 }
192
193 /**
194 * Get the reference to nodetest and predicate for
195 * parent or ancestor.
196 *
197 *
198 * @return The relative pattern expression.
199 */
200 public StepPattern getRelativePathPattern()
201 {
202 return m_relativePathPattern;
203 }
204
205 // /**
206 // * Set the list of predicate expressions for this pattern step.
207 // * @param predicates List of expression objects.
208 // */
209 // public void setPredicates(Expression[] predicates)
210 // {
211 // m_predicates = predicates;
212 // }
213
214 /**
215 * Set the list of predicate expressions for this pattern step.
216 * @return List of expression objects.
217 */
218 public Expression[] getPredicates()
219 {
220 return m_predicates;
221 }
222
223 /**
224 * The list of predicate expressions for this pattern step.
225 * @serial
226 */
227 Expression[] m_predicates;
228
229 /**
230 * Tell if this expression or it's subexpressions can traverse outside
231 * the current subtree.
232 *
233 * NOTE: Ancestors tests with predicates are problematic, and will require
234 * special treatment.
235 *
236 * @return true if traversal outside the context node's subtree can occur.
237 */
238 public boolean canTraverseOutsideSubtree()
239 {
240
241 int n = getPredicateCount();
242
243 for (int i = 0; i < n; i++)
244 {
245 if (getPredicate(i).canTraverseOutsideSubtree())
246 return true;
247 }
248
249 return false;
250 }
251
252 /**
253 * Get a predicate expression.
254 *
255 *
256 * @param i The index of the predicate.
257 *
258 * @return A predicate expression.
259 */
260 public Expression getPredicate(int i)
261 {
262 return m_predicates[i];
263 }
264
265 /**
266 * Get the number of predicates for this match pattern step.
267 *
268 *
269 * @return the number of predicates for this match pattern step.
270 */
271 public final int getPredicateCount()
272 {
273 return (null == m_predicates) ? 0 : m_predicates.length;
274 }
275
276 /**
277 * Set the predicates for this match pattern step.
278 *
279 *
280 * @param predicates An array of expressions that define predicates
281 * for this step.
282 */
283 public void setPredicates(Expression[] predicates)
284 {
285
286 m_predicates = predicates;
287 if(null != predicates)
288 {
289 for(int i = 0; i < predicates.length; i++)
290 {
291 predicates[i].exprSetParent(this);
292 }
293 }
294
295 calcScore();
296 }
297
298 /**
299 * Static calc of match score.
300 */
301 public void calcScore()
302 {
303
304 if ((getPredicateCount() > 0) || (null != m_relativePathPattern))
305 {
306 m_score = SCORE_OTHER;
307 }
308 else
309 super.calcScore();
310
311 if (null == m_targetString)
312 calcTargetString();
313 }
314
315 /**
316 * Execute this pattern step, including predicates.
317 *
318 *
319 * @param xctxt XPath runtime context.
320 * @param currentNode The current node context.
321 *
322 * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
323 * {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
324 * {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
325 * {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
326 * {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
327 *
328 * @throws javax.xml.transform.TransformerException
329 */
330 public XObject execute(XPathContext xctxt, int currentNode)
331 throws javax.xml.transform.TransformerException
332 {
333
334 DTM dtm = xctxt.getDTM(currentNode);
335
336 if (dtm != null)
337 {
338 int expType = dtm.getExpandedTypeID(currentNode);
339
340 return execute(xctxt, currentNode, dtm, expType);
341 }
342
343 return NodeTest.SCORE_NONE;
344 }
345
346 /**
347 * Execute this pattern step, including predicates.
348 *
349 *
350 * @param xctxt XPath runtime context.
351 *
352 * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
353 * {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
354 * {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
355 * {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
356 * {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
357 *
358 * @throws javax.xml.transform.TransformerException
359 */
360 public XObject execute(XPathContext xctxt)
361 throws javax.xml.transform.TransformerException
362 {
363 return execute(xctxt, xctxt.getCurrentNode());
364 }
365
366 /**
367 * Execute an expression in the XPath runtime context, and return the
368 * result of the expression.
369 *
370 *
371 * @param xctxt The XPath runtime context.
372 * @param currentNode The currentNode.
373 * @param dtm The DTM of the current node.
374 * @param expType The expanded type ID of the current node.
375 *
376 * @return The result of the expression in the form of a <code>XObject</code>.
377 *
378 * @throws javax.xml.transform.TransformerException if a runtime exception
379 * occurs.
380 */
381 public XObject execute(
382 XPathContext xctxt, int currentNode, DTM dtm, int expType)
383 throws javax.xml.transform.TransformerException
384 {
385
386 if (m_whatToShow == NodeTest.SHOW_BYFUNCTION)
387 {
388 if (null != m_relativePathPattern)
389 {
390 return m_relativePathPattern.execute(xctxt);
391 }
392 else
393 return NodeTest.SCORE_NONE;
394 }
395
396 XObject score;
397
398 score = super.execute(xctxt, currentNode, dtm, expType);
399
400 if (score == NodeTest.SCORE_NONE)
401 return NodeTest.SCORE_NONE;
402
403 if (getPredicateCount() != 0)
404 {
405 if (!executePredicates(xctxt, dtm, currentNode))
406 return NodeTest.SCORE_NONE;
407 }
408
409 if (null != m_relativePathPattern)
410 return m_relativePathPattern.executeRelativePathPattern(xctxt, dtm,
411 currentNode);
412
413 return score;
414 }
415
416 /**
417 * New Method to check whether the current node satisfies a position predicate
418 *
419 * @param xctxt The XPath runtime context.
420 * @param predPos Which predicate we're evaluating of foo[1][2][3].
421 * @param dtm The DTM of the current node.
422 * @param context The currentNode.
423 * @param pos The position being requested, i.e. the value returned by
424 * m_predicates[predPos].execute(xctxt).
425 *
426 * @return true of the position of the context matches pos, false otherwise.
427 */
428 private final boolean checkProximityPosition(XPathContext xctxt,
429 int predPos, DTM dtm, int context, int pos)
430 {
431
432 try
433 {
434 DTMAxisTraverser traverser =
435 dtm.getAxisTraverser(Axis.PRECEDINGSIBLING);
436
437 for (int child = traverser.first(context); DTM.NULL != child;
438 child = traverser.next(context, child))
439 {
440 try
441 {
442 xctxt.pushCurrentNode(child);
443
444 if (NodeTest.SCORE_NONE != super.execute(xctxt, child))
445 {
446 boolean pass = true;
447
448 try
449 {
450 xctxt.pushSubContextList(this);
451
452 for (int i = 0; i < predPos; i++)
453 {
454 xctxt.pushPredicatePos(i);
455 try
456 {
457 XObject pred = m_predicates[i].execute(xctxt);
458
459 try
460 {
461 if (XObject.CLASS_NUMBER == pred.getType())
462 {
463 throw new Error("Why: Should never have been called");
464 }
465 else if (!pred.boolWithSideEffects())
466 {
467 pass = false;
468
469 break;
470 }
471 }
472 finally
473 {
474 pred.detach();
475 }
476 }
477 finally
478 {
479 xctxt.popPredicatePos();
480 }
481 }
482 }
483 finally
484 {
485 xctxt.popSubContextList();
486 }
487
488 if (pass)
489 pos--;
490
491 if (pos < 1)
492 return false;
493 }
494 }
495 finally
496 {
497 xctxt.popCurrentNode();
498 }
499 }
500 }
501 catch (javax.xml.transform.TransformerException se)
502 {
503
504 // TODO: should keep throw sax exception...
505 throw new java.lang.RuntimeException(se.getMessage());
506 }
507
508 return (pos == 1);
509 }
510
511 /**
512 * Get the proximity position index of the current node based on this
513 * node test.
514 *
515 *
516 * @param xctxt XPath runtime context.
517 * @param predPos Which predicate we're evaluating of foo[1][2][3].
518 * @param findLast If true, don't terminate when the context node is found.
519 *
520 * @return the proximity position index of the current node based on the
521 * node test.
522 */
523 private final int getProximityPosition(XPathContext xctxt, int predPos,
524 boolean findLast)
525 {
526
527 int pos = 0;
528 int context = xctxt.getCurrentNode();
529 DTM dtm = xctxt.getDTM(context);
530 int parent = dtm.getParent(context);
531
532 try
533 {
534 DTMAxisTraverser traverser = dtm.getAxisTraverser(Axis.CHILD);
535
536 for (int child = traverser.first(parent); DTM.NULL != child;
537 child = traverser.next(parent, child))
538 {
539 try
540 {
541 xctxt.pushCurrentNode(child);
542
543 if (NodeTest.SCORE_NONE != super.execute(xctxt, child))
544 {
545 boolean pass = true;
546
547 try
548 {
549 xctxt.pushSubContextList(this);
550
551 for (int i = 0; i < predPos; i++)
552 {
553 xctxt.pushPredicatePos(i);
554 try
555 {
556 XObject pred = m_predicates[i].execute(xctxt);
557
558 try
559 {
560 if (XObject.CLASS_NUMBER == pred.getType())
561 {
562 if ((pos + 1) != (int) pred.numWithSideEffects())
563 {
564 pass = false;
565
566 break;
567 }
568 }
569 else if (!pred.boolWithSideEffects())
570 {
571 pass = false;
572
573 break;
574 }
575 }
576 finally
577 {
578 pred.detach();
579 }
580 }
581 finally
582 {
583 xctxt.popPredicatePos();
584 }
585 }
586 }
587 finally
588 {
589 xctxt.popSubContextList();
590 }
591
592 if (pass)
593 pos++;
594
595 if (!findLast && child == context)
596 {
597 return pos;
598 }
599 }
600 }
601 finally
602 {
603 xctxt.popCurrentNode();
604 }
605 }
606 }
607 catch (javax.xml.transform.TransformerException se)
608 {
609
610 // TODO: should keep throw sax exception...
611 throw new java.lang.RuntimeException(se.getMessage());
612 }
613
614 return pos;
615 }
616
617 /**
618 * Get the proximity position index of the current node based on this
619 * node test.
620 *
621 *
622 * @param xctxt XPath runtime context.
623 *
624 * @return the proximity position index of the current node based on the
625 * node test.
626 */
627 public int getProximityPosition(XPathContext xctxt)
628 {
629 return getProximityPosition(xctxt, xctxt.getPredicatePos(), false);
630 }
631
632 /**
633 * Get the count of the nodes that match the test, which is the proximity
634 * position of the last node that can pass this test in the sub context
635 * selection. In XSLT 1-based indexing, this count is the index of the last
636 * node.
637 *
638 *
639 * @param xctxt XPath runtime context.
640 *
641 * @return the count of the nodes that match the test.
642 */
643 public int getLastPos(XPathContext xctxt)
644 {
645 return getProximityPosition(xctxt, xctxt.getPredicatePos(), true);
646 }
647
648 /**
649 * Execute the match pattern step relative to another step.
650 *
651 *
652 * @param xctxt The XPath runtime context.
653 * @param dtm The DTM of the current node.
654 * @param currentNode The current node context.
655 *
656 * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
657 * {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
658 * {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
659 * {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
660 * {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
661 *
662 * @throws javax.xml.transform.TransformerException
663 */
664 protected final XObject executeRelativePathPattern(
665 XPathContext xctxt, DTM dtm, int currentNode)
666 throws javax.xml.transform.TransformerException
667 {
668
669 XObject score = NodeTest.SCORE_NONE;
670 int context = currentNode;
671 DTMAxisTraverser traverser;
672
673 traverser = dtm.getAxisTraverser(m_axis);
674
675 for (int relative = traverser.first(context); DTM.NULL != relative;
676 relative = traverser.next(context, relative))
677 {
678 try
679 {
680 xctxt.pushCurrentNode(relative);
681
682 score = execute(xctxt);
683
684 if (score != NodeTest.SCORE_NONE)
685 break;
686 }
687 finally
688 {
689 xctxt.popCurrentNode();
690 }
691 }
692
693 return score;
694 }
695
696 /**
697 * Execute the predicates on this step to determine if the current node
698 * should be filtered or accepted.
699 *
700 * @param xctxt The XPath runtime context.
701 * @param dtm The DTM of the current node.
702 * @param currentNode The current node context.
703 *
704 * @return true if the node should be accepted, false otherwise.
705 *
706 * @throws javax.xml.transform.TransformerException
707 */
708 protected final boolean executePredicates(
709 XPathContext xctxt, DTM dtm, int currentNode)
710 throws javax.xml.transform.TransformerException
711 {
712
713 boolean result = true;
714 boolean positionAlreadySeen = false;
715 int n = getPredicateCount();
716
717 try
718 {
719 xctxt.pushSubContextList(this);
720
721 for (int i = 0; i < n; i++)
722 {
723 xctxt.pushPredicatePos(i);
724
725 try
726 {
727 XObject pred = m_predicates[i].execute(xctxt);
728
729 try
730 {
731 if (XObject.CLASS_NUMBER == pred.getType())
732 {
733 int pos = (int) pred.num();
734
735 if (positionAlreadySeen)
736 {
737 result = (pos == 1);
738
739 break;
740 }
741 else
742 {
743 positionAlreadySeen = true;
744
745 if (!checkProximityPosition(xctxt, i, dtm, currentNode, pos))
746 {
747 result = false;
748
749 break;
750 }
751 }
752
753 }
754 else if (!pred.boolWithSideEffects())
755 {
756 result = false;
757
758 break;
759 }
760 }
761 finally
762 {
763 pred.detach();
764 }
765 }
766 finally
767 {
768 xctxt.popPredicatePos();
769 }
770 }
771 }
772 finally
773 {
774 xctxt.popSubContextList();
775 }
776
777 return result;
778 }
779
780 /**
781 * Get the string represenentation of this step for diagnostic purposes.
782 *
783 *
784 * @return A string representation of this step, built by reverse-engineering
785 * the contained info.
786 */
787 public String toString()
788 {
789
790 StringBuffer buf = new StringBuffer();
791
792 for (StepPattern pat = this; pat != null; pat = pat.m_relativePathPattern)
793 {
794 if (pat != this)
795 buf.append("/");
796
797 buf.append(Axis.getNames(pat.m_axis));
798 buf.append("::");
799
800 if (0x000005000 == pat.m_whatToShow)
801 {
802 buf.append("doc()");
803 }
804 else if (DTMFilter.SHOW_BYFUNCTION == pat.m_whatToShow)
805 {
806 buf.append("function()");
807 }
808 else if (DTMFilter.SHOW_ALL == pat.m_whatToShow)
809 {
810 buf.append("node()");
811 }
812 else if (DTMFilter.SHOW_TEXT == pat.m_whatToShow)
813 {
814 buf.append("text()");
815 }
816 else if (DTMFilter.SHOW_PROCESSING_INSTRUCTION == pat.m_whatToShow)
817 {
818 buf.append("processing-instruction(");
819
820 if (null != pat.m_name)
821 {
822 buf.append(pat.m_name);
823 }
824
825 buf.append(")");
826 }
827 else if (DTMFilter.SHOW_COMMENT == pat.m_whatToShow)
828 {
829 buf.append("comment()");
830 }
831 else if (null != pat.m_name)
832 {
833 if (DTMFilter.SHOW_ATTRIBUTE == pat.m_whatToShow)
834 {
835 buf.append("@");
836 }
837
838 if (null != pat.m_namespace)
839 {
840 buf.append("{");
841 buf.append(pat.m_namespace);
842 buf.append("}");
843 }
844
845 buf.append(pat.m_name);
846 }
847 else if (DTMFilter.SHOW_ATTRIBUTE == pat.m_whatToShow)
848 {
849 buf.append("@");
850 }
851 else if ((DTMFilter.SHOW_DOCUMENT | DTMFilter.SHOW_DOCUMENT_FRAGMENT)
852 == pat.m_whatToShow)
853 {
854 buf.append("doc-root()");
855 }
856 else
857 {
858 buf.append("?" + Integer.toHexString(pat.m_whatToShow));
859 }
860
861 if (null != pat.m_predicates)
862 {
863 for (int i = 0; i < pat.m_predicates.length; i++)
864 {
865 buf.append("[");
866 buf.append(pat.m_predicates[i]);
867 buf.append("]");
868 }
869 }
870 }
871
872 return buf.toString();
873 }
874
875 /** Set to true to send diagnostics about pattern matches to the consol. */
876 private static final boolean DEBUG_MATCHES = false;
877
878 /**
879 * Get the match score of the given node.
880 *
881 * @param xctxt The XPath runtime context.
882 * @param context The node to be tested.
883 *
884 * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
885 * {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
886 * {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
887 * {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
888 * {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
889 *
890 * @throws javax.xml.transform.TransformerException
891 */
892 public double getMatchScore(XPathContext xctxt, int context)
893 throws javax.xml.transform.TransformerException
894 {
895
896 xctxt.pushCurrentNode(context);
897 xctxt.pushCurrentExpressionNode(context);
898
899 try
900 {
901 XObject score = execute(xctxt);
902
903 return score.num();
904 }
905 finally
906 {
907 xctxt.popCurrentNode();
908 xctxt.popCurrentExpressionNode();
909 }
910
911 // return XPath.MATCH_SCORE_NONE;
912 }
913
914 /**
915 * Set the axis that this step should follow.
916 *
917 *
918 * @param axis The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
919 */
920 public void setAxis(int axis)
921 {
922 m_axis = axis;
923 }
924
925 /**
926 * Get the axis that this step follows.
927 *
928 *
929 * @return The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
930 */
931 public int getAxis()
932 {
933 return m_axis;
934 }
935
936 class PredOwner implements ExpressionOwner
937 {
938 int m_index;
939
940 PredOwner(int index)
941 {
942 m_index = index;
943 }
944
945 /**
946 * @see ExpressionOwner#getExpression()
947 */
948 public Expression getExpression()
949 {
950 return m_predicates[m_index];
951 }
952
953
954 /**
955 * @see ExpressionOwner#setExpression(Expression)
956 */
957 public void setExpression(Expression exp)
958 {
959 exp.exprSetParent(StepPattern.this);
960 m_predicates[m_index] = exp;
961 }
962 }
963
964 /**
965 * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
966 */
967 public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
968 {
969 if(visitor.visitMatchPattern(owner, this))
970 {
971 callSubtreeVisitors(visitor);
972 }
973 }
974
975 /**
976 * Call the visitors on the subtree. Factored out from callVisitors
977 * so it may be called by derived classes.
978 */
979 protected void callSubtreeVisitors(XPathVisitor visitor)
980 {
981 if (null != m_predicates)
982 {
983 int n = m_predicates.length;
984 for (int i = 0; i < n; i++)
985 {
986 ExpressionOwner predOwner = new PredOwner(i);
987 if (visitor.visitPredicate(predOwner, m_predicates[i]))
988 {
989 m_predicates[i].callVisitors(predOwner, visitor);
990 }
991 }
992 }
993 if (null != m_relativePathPattern)
994 {
995 m_relativePathPattern.callVisitors(this, visitor);
996 }
997 }
998
999
1000 /**
1001 * @see ExpressionOwner#getExpression()
1002 */
1003 public Expression getExpression()
1004 {
1005 return m_relativePathPattern;
1006 }
1007
1008 /**
1009 * @see ExpressionOwner#setExpression(Expression)
1010 */
1011 public void setExpression(Expression exp)
1012 {
1013 exp.exprSetParent(this);
1014 m_relativePathPattern = (StepPattern)exp;
1015 }
1016
1017 /**
1018 * @see Expression#deepEquals(Expression)
1019 */
1020 public boolean deepEquals(Expression expr)
1021 {
1022 if(!super.deepEquals(expr))
1023 return false;
1024
1025 StepPattern sp = (StepPattern)expr;
1026
1027 if (null != m_predicates)
1028 {
1029 int n = m_predicates.length;
1030 if ((null == sp.m_predicates) || (sp.m_predicates.length != n))
1031 return false;
1032 for (int i = 0; i < n; i++)
1033 {
1034 if (!m_predicates[i].deepEquals(sp.m_predicates[i]))
1035 return false;
1036 }
1037 }
1038 else if (null != sp.m_predicates)
1039 return false;
1040
1041 if(null != m_relativePathPattern)
1042 {
1043 if(!m_relativePathPattern.deepEquals(sp.m_relativePathPattern))
1044 return false;
1045 }
1046 else if(sp.m_relativePathPattern != null)
1047 return false;
1048
1049 return true;
1050 }
1051
1052
1053 }