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