View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.jcr.xpath;
25  
26  import java.util.ArrayList;
27  import java.util.Iterator;
28  import java.util.List;
29  import org.modeshape.common.collection.ReadOnlyIterator;
30  import org.modeshape.common.util.CheckArg;
31  import org.modeshape.common.util.HashCode;
32  import org.modeshape.common.util.ObjectUtil;
33  import org.modeshape.graph.query.model.Operator;
34  import org.modeshape.graph.query.model.Order;
35  
36  /**
37   * Abstract syntax components of an XPath query. The supported grammar is defined by JCR 1.0, and is a subset of what is allowed
38   * by the W3C XPath 2.0 specification.
39   * 
40   * @see XPathParser#parseXPath(String)
41   */
42  public class XPath {
43  
44      public static enum NodeComparisonOperator {
45          IS,
46          PRECEDES,
47          FOLLOWS;
48      }
49  
50      public static abstract class Component {
51          /**
52           * Return the collapsable form
53           * 
54           * @return the collapsed form of th is component; never null and possibly the same as this
55           */
56          public Component collapse() {
57              return this;
58          }
59      }
60  
61      public static abstract class UnaryComponent extends Component {
62          protected final Component wrapped;
63  
64          public UnaryComponent( Component wrapped ) {
65              this.wrapped = wrapped;
66          }
67      }
68  
69      public static class Negation extends UnaryComponent {
70          public Negation( Component wrapped ) {
71              super(wrapped);
72          }
73  
74          public Component getNegated() {
75              return wrapped;
76          }
77  
78          /**
79           * {@inheritDoc}
80           * 
81           * @see java.lang.Object#toString()
82           */
83          @Override
84          public String toString() {
85              return "-" + wrapped;
86          }
87  
88          /**
89           * {@inheritDoc}
90           * 
91           * @see java.lang.Object#equals(java.lang.Object)
92           */
93          @Override
94          public boolean equals( Object obj ) {
95              if (obj == this) return true;
96              if (obj instanceof Negation) {
97                  Negation that = (Negation)obj;
98                  return this.wrapped.equals(that.wrapped);
99              }
100             return false;
101         }
102     }
103 
104     public static abstract class BinaryComponent extends Component {
105         private final Component left;
106         private final Component right;
107 
108         public BinaryComponent( Component left,
109                                 Component right ) {
110             this.left = left;
111             this.right = right;
112         }
113 
114         /**
115          * @return left
116          */
117         public Component getLeft() {
118             return left;
119         }
120 
121         /**
122          * @return right
123          */
124         public Component getRight() {
125             return right;
126         }
127     }
128 
129     public static class Comparison extends BinaryComponent {
130         private final Operator operator;
131 
132         public Comparison( Component left,
133                            Operator operator,
134                            Component right ) {
135             super(left, right);
136             this.operator = operator;
137         }
138 
139         /**
140          * @return operator
141          */
142         public Operator getOperator() {
143             return operator;
144         }
145 
146         /**
147          * {@inheritDoc}
148          * 
149          * @see org.modeshape.jcr.xpath.XPath.Component#collapse()
150          */
151         @Override
152         public Component collapse() {
153             return new Comparison(getLeft().collapse(), operator, getRight().collapse());
154         }
155 
156         /**
157          * {@inheritDoc}
158          * 
159          * @see java.lang.Object#toString()
160          */
161         @Override
162         public String toString() {
163             return getLeft() + " " + operator + " " + getRight();
164         }
165 
166         /**
167          * {@inheritDoc}
168          * 
169          * @see java.lang.Object#equals(java.lang.Object)
170          */
171         @Override
172         public boolean equals( Object obj ) {
173             if (obj == this) return true;
174             if (obj instanceof Comparison) {
175                 Comparison that = (Comparison)obj;
176                 if (this.operator != that.operator) return false;
177                 return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
178             }
179             return false;
180         }
181     }
182 
183     public static class NodeComparison extends BinaryComponent {
184         private final NodeComparisonOperator operator;
185 
186         public NodeComparison( Component left,
187                                NodeComparisonOperator operator,
188                                Component right ) {
189             super(left, right);
190             this.operator = operator;
191         }
192 
193         /**
194          * {@inheritDoc}
195          * 
196          * @see org.modeshape.jcr.xpath.XPath.Component#collapse()
197          */
198         @Override
199         public Component collapse() {
200             return new NodeComparison(getLeft().collapse(), operator, getRight().collapse());
201         }
202 
203         /**
204          * {@inheritDoc}
205          * 
206          * @see java.lang.Object#toString()
207          */
208         @Override
209         public String toString() {
210             return getLeft() + " " + operator + " " + getRight();
211         }
212 
213         /**
214          * {@inheritDoc}
215          * 
216          * @see java.lang.Object#equals(java.lang.Object)
217          */
218         @Override
219         public boolean equals( Object obj ) {
220             if (obj == this) return true;
221             if (obj instanceof NodeComparison) {
222                 NodeComparison that = (NodeComparison)obj;
223                 if (this.operator != that.operator) return false;
224                 return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
225             }
226             return false;
227         }
228     }
229 
230     public static class Add extends BinaryComponent {
231         public Add( Component left,
232                     Component right ) {
233             super(left, right);
234         }
235 
236         /**
237          * {@inheritDoc}
238          * 
239          * @see org.modeshape.jcr.xpath.XPath.Component#collapse()
240          */
241         @Override
242         public Component collapse() {
243             return new Add(getLeft().collapse(), getRight().collapse());
244         }
245 
246         /**
247          * {@inheritDoc}
248          * 
249          * @see java.lang.Object#toString()
250          */
251         @Override
252         public String toString() {
253             return getLeft() + " + " + getRight();
254         }
255 
256         /**
257          * {@inheritDoc}
258          * 
259          * @see java.lang.Object#equals(java.lang.Object)
260          */
261         @Override
262         public boolean equals( Object obj ) {
263             if (obj == this) return true;
264             if (obj instanceof Add) {
265                 Add that = (Add)obj;
266                 return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
267             }
268             return false;
269         }
270     }
271 
272     public static class Subtract extends BinaryComponent {
273         public Subtract( Component left,
274                          Component right ) {
275             super(left, right);
276         }
277 
278         /**
279          * {@inheritDoc}
280          * 
281          * @see org.modeshape.jcr.xpath.XPath.Component#collapse()
282          */
283         @Override
284         public Component collapse() {
285             return new Subtract(getLeft().collapse(), getRight().collapse());
286         }
287 
288         /**
289          * {@inheritDoc}
290          * 
291          * @see java.lang.Object#toString()
292          */
293         @Override
294         public String toString() {
295             return getLeft() + " - " + getRight();
296         }
297 
298         /**
299          * {@inheritDoc}
300          * 
301          * @see java.lang.Object#equals(java.lang.Object)
302          */
303         @Override
304         public boolean equals( Object obj ) {
305             if (obj == this) return true;
306             if (obj instanceof Subtract) {
307                 Subtract that = (Subtract)obj;
308                 return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
309             }
310             return false;
311         }
312     }
313 
314     public static class And extends BinaryComponent {
315         public And( Component left,
316                     Component right ) {
317             super(left, right);
318         }
319 
320         /**
321          * {@inheritDoc}
322          * 
323          * @see org.modeshape.jcr.xpath.XPath.Component#collapse()
324          */
325         @Override
326         public Component collapse() {
327             return new And(getLeft().collapse(), getRight().collapse());
328         }
329 
330         /**
331          * {@inheritDoc}
332          * 
333          * @see java.lang.Object#toString()
334          */
335         @Override
336         public String toString() {
337             return getLeft() + " and " + getRight();
338         }
339 
340         /**
341          * {@inheritDoc}
342          * 
343          * @see java.lang.Object#equals(java.lang.Object)
344          */
345         @Override
346         public boolean equals( Object obj ) {
347             if (obj == this) return true;
348             if (obj instanceof And) {
349                 And that = (And)obj;
350                 return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
351             }
352             return false;
353         }
354     }
355 
356     public static class Union extends BinaryComponent {
357         public Union( Component left,
358                       Component right ) {
359             super(left, right);
360         }
361 
362         /**
363          * {@inheritDoc}
364          * 
365          * @see java.lang.Object#toString()
366          */
367         @Override
368         public String toString() {
369             return getLeft() + " union " + getRight();
370         }
371 
372         /**
373          * {@inheritDoc}
374          * 
375          * @see java.lang.Object#equals(java.lang.Object)
376          */
377         @Override
378         public boolean equals( Object obj ) {
379             if (obj == this) return true;
380             if (obj instanceof Union) {
381                 Union that = (Union)obj;
382                 return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
383             }
384             return false;
385         }
386     }
387 
388     public static class Intersect extends BinaryComponent {
389         public Intersect( Component left,
390                           Component right ) {
391             super(left, right);
392         }
393 
394         /**
395          * {@inheritDoc}
396          * 
397          * @see java.lang.Object#toString()
398          */
399         @Override
400         public String toString() {
401             return getLeft() + " intersect " + getRight();
402         }
403 
404         /**
405          * {@inheritDoc}
406          * 
407          * @see java.lang.Object#equals(java.lang.Object)
408          */
409         @Override
410         public boolean equals( Object obj ) {
411             if (obj == this) return true;
412             if (obj instanceof Intersect) {
413                 Intersect that = (Intersect)obj;
414                 return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
415             }
416             return false;
417         }
418     }
419 
420     public static class Except extends BinaryComponent {
421         public Except( Component left,
422                        Component right ) {
423             super(left, right);
424         }
425 
426         /**
427          * {@inheritDoc}
428          * 
429          * @see java.lang.Object#toString()
430          */
431         @Override
432         public String toString() {
433             return getLeft() + " except " + getRight();
434         }
435 
436         /**
437          * {@inheritDoc}
438          * 
439          * @see java.lang.Object#equals(java.lang.Object)
440          */
441         @Override
442         public boolean equals( Object obj ) {
443             if (obj == this) return true;
444             if (obj instanceof Except) {
445                 Except that = (Except)obj;
446                 return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
447             }
448             return false;
449         }
450     }
451 
452     public static class Or extends BinaryComponent {
453         public Or( Component left,
454                    Component right ) {
455             super(left, right);
456         }
457 
458         /**
459          * {@inheritDoc}
460          * 
461          * @see org.modeshape.jcr.xpath.XPath.Component#collapse()
462          */
463         @Override
464         public Component collapse() {
465             return new Or(getLeft().collapse(), getRight().collapse());
466         }
467 
468         /**
469          * {@inheritDoc}
470          * 
471          * @see java.lang.Object#toString()
472          */
473         @Override
474         public String toString() {
475             return getLeft() + " or " + getRight();
476         }
477 
478         /**
479          * {@inheritDoc}
480          * 
481          * @see java.lang.Object#equals(java.lang.Object)
482          */
483         @Override
484         public boolean equals( Object obj ) {
485             if (obj == this) return true;
486             if (obj instanceof Or) {
487                 Or that = (Or)obj;
488                 return this.getLeft().equals(that.getLeft()) && this.getRight().equals(that.getRight());
489             }
490             return false;
491         }
492     }
493 
494     public static class ContextItem extends Component {
495         /**
496          * {@inheritDoc}
497          * 
498          * @see java.lang.Object#equals(java.lang.Object)
499          */
500         @Override
501         public boolean equals( Object obj ) {
502             return obj == this || obj instanceof ContextItem;
503         }
504 
505         /**
506          * {@inheritDoc}
507          * 
508          * @see java.lang.Object#toString()
509          */
510         @Override
511         public String toString() {
512             return ".";
513         }
514     }
515 
516     public static class Literal extends Component {
517         private final String value;
518 
519         public Literal( String value ) {
520             this.value = value;
521         }
522 
523         /**
524          * @return value
525          */
526         public String getValue() {
527             return value;
528         }
529 
530         public boolean isInteger() {
531             try {
532                 Integer.parseInt(value);
533                 return true;
534             } catch (NumberFormatException e) {
535                 return false;
536             }
537         }
538 
539         public int getValueAsInteger() {
540             return Integer.parseInt(value);
541         }
542 
543         /**
544          * {@inheritDoc}
545          * 
546          * @see java.lang.Object#equals(java.lang.Object)
547          */
548         @Override
549         public boolean equals( Object obj ) {
550             if (obj == this) return true;
551             if (obj instanceof Literal) {
552                 return this.value.equals(((Literal)obj).value);
553             }
554             return false;
555         }
556 
557         /**
558          * {@inheritDoc}
559          * 
560          * @see java.lang.Object#toString()
561          */
562         @Override
563         public String toString() {
564             return value;
565         }
566     }
567 
568     public static class FunctionCall extends Component {
569         private final NameTest name;
570         private final List<Component> arguments;
571 
572         public FunctionCall( NameTest name,
573                              List<Component> arguments ) {
574             assert name != null;
575             assert arguments != null;
576             this.name = name;
577             this.arguments = arguments;
578         }
579 
580         /**
581          * @return name
582          */
583         public NameTest getName() {
584             return name;
585         }
586 
587         /**
588          * @return arguments
589          */
590         public List<Component> getParameters() {
591             return arguments;
592         }
593 
594         /**
595          * {@inheritDoc}
596          * 
597          * @see org.modeshape.jcr.xpath.XPath.Component#collapse()
598          */
599         @Override
600         public Component collapse() {
601             List<Component> args = new ArrayList<Component>(arguments.size());
602             for (Component arg : arguments) {
603                 args.add(arg.collapse());
604             }
605             return new FunctionCall(name, args);
606         }
607 
608         /**
609          * {@inheritDoc}
610          * 
611          * @see java.lang.Object#equals(java.lang.Object)
612          */
613         @Override
614         public boolean equals( Object obj ) {
615             if (obj == this) return true;
616             if (obj instanceof FunctionCall) {
617                 FunctionCall that = (FunctionCall)obj;
618                 return this.name.equals(that.name) && this.arguments.equals(that.arguments);
619             }
620             return false;
621         }
622 
623         /**
624          * {@inheritDoc}
625          * 
626          * @see java.lang.Object#toString()
627          */
628         @Override
629         public String toString() {
630             return name + "(" + asString(arguments, ",") + ")";
631         }
632     }
633 
634     protected static String asString( Iterable<?> components,
635                                       String delimiter ) {
636         StringBuilder sb = new StringBuilder();
637         for (Object component : components) {
638             if (sb.length() != 0) sb.append(delimiter);
639             sb.append(component);
640         }
641         return sb.toString();
642     }
643 
644     public static class PathExpression extends Component implements Iterable<StepExpression> {
645         private final List<StepExpression> steps;
646         private final boolean relative;
647         private final OrderBy orderBy;
648 
649         public PathExpression( boolean relative,
650                                List<StepExpression> steps,
651                                OrderBy orderBy ) {
652             this.steps = steps;
653             this.relative = relative;
654             this.orderBy = orderBy;
655         }
656 
657         /**
658          * @return relative
659          */
660         public boolean isRelative() {
661             return relative;
662         }
663 
664         /**
665          * Get the order-by clause.
666          * 
667          * @return the order-by clause, or null if there is no such clause
668          */
669         public OrderBy getOrderBy() {
670             return orderBy;
671         }
672 
673         /**
674          * @return steps
675          */
676         public List<StepExpression> getSteps() {
677             return steps;
678         }
679 
680         public StepExpression getLastStep() {
681             return steps.isEmpty() ? null : steps.get(steps.size() - 1);
682         }
683 
684         public PathExpression withoutLast() {
685             assert !steps.isEmpty();
686             return new PathExpression(relative, steps.subList(0, steps.size() - 1), orderBy);
687         }
688 
689         public PathExpression withoutFirst() {
690             assert !steps.isEmpty();
691             return new PathExpression(relative, steps.subList(1, steps.size()), orderBy);
692         }
693 
694         /**
695          * {@inheritDoc}
696          * 
697          * @see java.lang.Iterable#iterator()
698          */
699         public Iterator<StepExpression> iterator() {
700             return steps.iterator();
701         }
702 
703         @Override
704         public Component collapse() {
705             return steps.size() == 1 ? steps.get(0).collapse() : this;
706         }
707 
708         /**
709          * {@inheritDoc}
710          * 
711          * @see java.lang.Object#equals(java.lang.Object)
712          */
713         @Override
714         public boolean equals( Object obj ) {
715             if (obj == this) return true;
716             if (obj instanceof PathExpression) {
717                 PathExpression that = (PathExpression)obj;
718                 if (this.relative != that.relative) return false;
719                 if (this.orderBy != null && !this.orderBy.equals(that.orderBy)) return false;
720                 if (this.orderBy == null && that.orderBy != null) return false;
721                 return this.steps.equals(that.steps);
722             }
723             return false;
724         }
725 
726         /**
727          * {@inheritDoc}
728          * 
729          * @see java.lang.Object#toString()
730          */
731         @Override
732         public String toString() {
733             return (relative ? "" : "/") + asString(steps, "/");
734         }
735     }
736 
737     public static abstract class StepExpression extends Component {
738     }
739 
740     public static class FilterStep extends StepExpression {
741         private final Component primaryExpression;
742         private final List<Component> predicates;
743 
744         public FilterStep( Component primaryExpression,
745                            List<Component> predicates ) {
746             assert primaryExpression != null;
747             assert predicates != null;
748             this.primaryExpression = primaryExpression;
749             this.predicates = predicates;
750         }
751 
752         /**
753          * @return nodeTest
754          */
755         public Component getPrimaryExpression() {
756             return primaryExpression;
757         }
758 
759         /**
760          * @return predicates
761          */
762         public List<Component> getPredicates() {
763             return predicates;
764         }
765 
766         @Override
767         public Component collapse() {
768             return predicates.isEmpty() ? primaryExpression.collapse() : this;
769         }
770 
771         /**
772          * {@inheritDoc}
773          * 
774          * @see java.lang.Object#equals(java.lang.Object)
775          */
776         @Override
777         public boolean equals( Object obj ) {
778             if (obj == this) return true;
779             if (obj instanceof FilterStep) {
780                 FilterStep that = (FilterStep)obj;
781                 return this.primaryExpression.equals(that.primaryExpression) && this.predicates.equals(that.predicates);
782             }
783             return false;
784         }
785 
786         /**
787          * {@inheritDoc}
788          * 
789          * @see java.lang.Object#toString()
790          */
791         @Override
792         public String toString() {
793             return primaryExpression + (predicates.isEmpty() ? "" : predicates.toString());
794         }
795     }
796 
797     public static class DescendantOrSelf extends StepExpression {
798         /**
799          * {@inheritDoc}
800          * 
801          * @see java.lang.Object#equals(java.lang.Object)
802          */
803         @Override
804         public boolean equals( Object obj ) {
805             return obj == this || obj instanceof DescendantOrSelf;
806         }
807 
808         /**
809          * {@inheritDoc}
810          * 
811          * @see java.lang.Object#toString()
812          */
813         @Override
814         public String toString() {
815             return "descendant-or-self::node()";
816         }
817     }
818 
819     public static class AxisStep extends StepExpression {
820         private final NodeTest nodeTest;
821         private final List<Component> predicates;
822 
823         public AxisStep( NodeTest nodeTest,
824                          List<Component> predicates ) {
825             assert nodeTest != null;
826             assert predicates != null;
827             this.nodeTest = nodeTest;
828             this.predicates = predicates;
829         }
830 
831         /**
832          * @return nodeTest
833          */
834         public NodeTest getNodeTest() {
835             return nodeTest;
836         }
837 
838         /**
839          * @return predicates
840          */
841         public List<Component> getPredicates() {
842             return predicates;
843         }
844 
845         @Override
846         public Component collapse() {
847             return predicates.isEmpty() ? nodeTest.collapse() : this;
848         }
849 
850         /**
851          * {@inheritDoc}
852          * 
853          * @see java.lang.Object#equals(java.lang.Object)
854          */
855         @Override
856         public boolean equals( Object obj ) {
857             if (obj == this) return true;
858             if (obj instanceof AxisStep) {
859                 AxisStep that = (AxisStep)obj;
860                 return this.nodeTest.equals(that.nodeTest) && this.predicates.equals(that.predicates);
861             }
862             return false;
863         }
864 
865         /**
866          * {@inheritDoc}
867          * 
868          * @see java.lang.Object#toString()
869          */
870         @Override
871         public String toString() {
872             return nodeTest + (predicates.isEmpty() ? "" : predicates.toString());
873         }
874     }
875 
876     public static class ParenthesizedExpression extends Component {
877         private final Component wrapped;
878 
879         public ParenthesizedExpression() {
880             this.wrapped = null;
881         }
882 
883         public ParenthesizedExpression( Component wrapped ) {
884             this.wrapped = wrapped; // may be null
885         }
886 
887         /**
888          * @return wrapped
889          */
890         public Component getWrapped() {
891             return wrapped;
892         }
893 
894         /**
895          * {@inheritDoc}
896          * 
897          * @see org.modeshape.jcr.xpath.XPath.Component#collapse()
898          */
899         @Override
900         public Component collapse() {
901             return wrapped instanceof BinaryComponent ? this : wrapped;
902         }
903 
904         /**
905          * {@inheritDoc}
906          * 
907          * @see java.lang.Object#equals(java.lang.Object)
908          */
909         @Override
910         public boolean equals( Object obj ) {
911             if (obj == this) return true;
912             if (obj instanceof ParenthesizedExpression) {
913                 ParenthesizedExpression that = (ParenthesizedExpression)obj;
914                 return this.wrapped.equals(that.wrapped);
915             }
916             return false;
917         }
918 
919         /**
920          * {@inheritDoc}
921          * 
922          * @see java.lang.Object#toString()
923          */
924         @Override
925         public String toString() {
926             return "(" + wrapped + ")";
927         }
928     }
929 
930     public static abstract class NodeTest extends Component {
931     }
932 
933     public static abstract class KindTest extends NodeTest {
934     }
935 
936     public static class NameTest extends NodeTest {
937         private final String prefixTest;
938         private final String localTest;
939 
940         public NameTest( String prefixTest,
941                          String localTest ) {
942             this.prefixTest = prefixTest;
943             this.localTest = localTest;
944         }
945 
946         /**
947          * @return the prefix criteria, or null if the prefix criteria is a wildcard
948          */
949         public String getPrefixTest() {
950             return prefixTest;
951         }
952 
953         /**
954          * @return the local name criteria, or null if the local name criteria is a wildcard
955          */
956         public String getLocalTest() {
957             return localTest;
958         }
959 
960         /**
961          * Determine if this name test exactly matches the supplied prefix and local name values.
962          * 
963          * @param prefix the prefix; may be null
964          * @param local the local name; may be null
965          * @return true if this name matches the supplied values, or false otherwise.
966          */
967         public boolean matches( String prefix,
968                                 String local ) {
969             if (this.prefixTest != null && !this.prefixTest.equals(prefix)) return false;
970             if (this.localTest != null && !this.localTest.equals(local)) return false;
971             return true;
972         }
973 
974         /**
975          * Return whether this represents a wildcard, meaning both {@link #getPrefixTest()} and {@link #getLocalTest()} are null.
976          * 
977          * @return true if this is a wildcard name test.
978          */
979         public boolean isWildcard() {
980             return prefixTest == null && localTest == null;
981         }
982 
983         /**
984          * {@inheritDoc}
985          * 
986          * @see java.lang.Object#hashCode()
987          */
988         @Override
989         public int hashCode() {
990             return HashCode.compute(this.prefixTest, this.localTest);
991         }
992 
993         /**
994          * {@inheritDoc}
995          * 
996          * @see java.lang.Object#equals(java.lang.Object)
997          */
998         @Override
999         public boolean equals( Object obj ) {
1000             if (obj == this) return true;
1001             if (obj instanceof NameTest) {
1002                 NameTest that = (NameTest)obj;
1003                 return ObjectUtil.isEqualWithNulls(this.prefixTest, that.prefixTest)
1004                        && ObjectUtil.isEqualWithNulls(this.localTest, that.localTest);
1005             }
1006             return false;
1007         }
1008 
1009         /**
1010          * {@inheritDoc}
1011          * 
1012          * @see java.lang.Object#toString()
1013          */
1014         @Override
1015         public String toString() {
1016             String local = localTest != null ? localTest : "*";
1017             return prefixTest == null ? local : (prefixTest + ":" + local);
1018         }
1019     }
1020 
1021     public static class AttributeNameTest extends NodeTest {
1022         private final NameTest nameTest;
1023 
1024         public AttributeNameTest( NameTest nameTest ) {
1025             this.nameTest = nameTest;
1026         }
1027 
1028         /**
1029          * @return nodeTest
1030          */
1031         public NameTest getNameTest() {
1032             return nameTest;
1033         }
1034 
1035         /**
1036          * {@inheritDoc}
1037          * 
1038          * @see XPath.NameTest#toString()
1039          */
1040         @Override
1041         public String toString() {
1042             return "@" + nameTest;
1043         }
1044 
1045         /**
1046          * {@inheritDoc}
1047          * 
1048          * @see java.lang.Object#equals(java.lang.Object)
1049          */
1050         @Override
1051         public boolean equals( Object obj ) {
1052             if (obj == this) return true;
1053             if (obj instanceof AttributeNameTest) {
1054                 AttributeNameTest that = (AttributeNameTest)obj;
1055                 return this.nameTest.equals(that.nameTest);
1056             }
1057             return false;
1058         }
1059 
1060     }
1061 
1062     public static class AnyKindTest extends KindTest {
1063         /**
1064          * {@inheritDoc}
1065          * 
1066          * @see java.lang.Object#equals(java.lang.Object)
1067          */
1068         @Override
1069         public boolean equals( Object obj ) {
1070             return obj == this || obj instanceof AnyKindTest;
1071         }
1072 
1073         /**
1074          * {@inheritDoc}
1075          * 
1076          * @see java.lang.Object#toString()
1077          */
1078         @Override
1079         public String toString() {
1080             return "node()";
1081         }
1082     }
1083 
1084     public static class TextTest extends KindTest {
1085         /**
1086          * {@inheritDoc}
1087          * 
1088          * @see java.lang.Object#equals(java.lang.Object)
1089          */
1090         @Override
1091         public boolean equals( Object obj ) {
1092             return obj == this || obj instanceof TextTest;
1093         }
1094 
1095         /**
1096          * {@inheritDoc}
1097          * 
1098          * @see java.lang.Object#toString()
1099          */
1100         @Override
1101         public String toString() {
1102             return "text()";
1103         }
1104     }
1105 
1106     public static class CommentTest extends KindTest {
1107         /**
1108          * {@inheritDoc}
1109          * 
1110          * @see java.lang.Object#equals(java.lang.Object)
1111          */
1112         @Override
1113         public boolean equals( Object obj ) {
1114             return obj == this || obj instanceof CommentTest;
1115         }
1116 
1117         /**
1118          * {@inheritDoc}
1119          * 
1120          * @see java.lang.Object#toString()
1121          */
1122         @Override
1123         public String toString() {
1124             return "comment()";
1125         }
1126     }
1127 
1128     public static class ProcessingInstructionTest extends KindTest {
1129         private final String nameOrStringLiteral;
1130 
1131         public ProcessingInstructionTest( String nameOrStringLiteral ) {
1132             this.nameOrStringLiteral = nameOrStringLiteral;
1133         }
1134 
1135         /**
1136          * @return nameOrStringLiteral
1137          */
1138         public String getNameOrStringLiteral() {
1139             return nameOrStringLiteral;
1140         }
1141 
1142         /**
1143          * {@inheritDoc}
1144          * 
1145          * @see java.lang.Object#equals(java.lang.Object)
1146          */
1147         @Override
1148         public boolean equals( Object obj ) {
1149             if (obj == this) return true;
1150             if (obj instanceof ProcessingInstructionTest) {
1151                 ProcessingInstructionTest that = (ProcessingInstructionTest)obj;
1152                 return this.nameOrStringLiteral.equals(that.nameOrStringLiteral);
1153             }
1154             return false;
1155         }
1156 
1157         /**
1158          * {@inheritDoc}
1159          * 
1160          * @see java.lang.Object#toString()
1161          */
1162         @Override
1163         public String toString() {
1164             return "processing-instruction(" + nameOrStringLiteral + ")";
1165         }
1166     }
1167 
1168     public static class DocumentTest extends KindTest {
1169         private KindTest elementOrSchemaElementTest;
1170 
1171         public DocumentTest( ElementTest elementTest ) {
1172             CheckArg.isNotNull(elementTest, "elementTest");
1173             this.elementOrSchemaElementTest = elementTest;
1174         }
1175 
1176         public DocumentTest( SchemaElementTest schemaElementTest ) {
1177             CheckArg.isNotNull(schemaElementTest, "schemaElementTest");
1178             this.elementOrSchemaElementTest = schemaElementTest;
1179         }
1180 
1181         /**
1182          * @return elementOrSchemaElementTest
1183          */
1184         public ElementTest getElementTest() {
1185             return elementOrSchemaElementTest instanceof ElementTest ? (ElementTest)elementOrSchemaElementTest : null;
1186         }
1187 
1188         /**
1189          * @return elementOrSchemaElementTest
1190          */
1191         public SchemaElementTest getSchemaElementTest() {
1192             return elementOrSchemaElementTest instanceof SchemaElementTest ? (SchemaElementTest)elementOrSchemaElementTest : null;
1193         }
1194 
1195         /**
1196          * {@inheritDoc}
1197          * 
1198          * @see java.lang.Object#equals(java.lang.Object)
1199          */
1200         @Override
1201         public boolean equals( Object obj ) {
1202             if (obj == this) return true;
1203             if (obj instanceof DocumentTest) {
1204                 DocumentTest that = (DocumentTest)obj;
1205                 return this.elementOrSchemaElementTest.equals(that.elementOrSchemaElementTest);
1206             }
1207             return false;
1208         }
1209 
1210         /**
1211          * {@inheritDoc}
1212          * 
1213          * @see java.lang.Object#toString()
1214          */
1215         @Override
1216         public String toString() {
1217             return "document-node(" + elementOrSchemaElementTest + ")";
1218         }
1219     }
1220 
1221     public static class AttributeTest extends KindTest {
1222         private final NameTest attributeNameOrWildcard;
1223         private final NameTest typeName;
1224 
1225         public AttributeTest( NameTest attributeNameOrWildcard,
1226                               NameTest typeName ) {
1227             this.attributeNameOrWildcard = attributeNameOrWildcard;
1228             this.typeName = typeName;
1229         }
1230 
1231         /**
1232          *@return the attribute name, which may be a wilcard
1233          */
1234         public NameTest getAttributeName() {
1235             return attributeNameOrWildcard;
1236         }
1237 
1238         /**
1239          * @return typeName
1240          */
1241         public NameTest getTypeName() {
1242             return typeName;
1243         }
1244 
1245         /**
1246          * {@inheritDoc}
1247          * 
1248          * @see java.lang.Object#equals(java.lang.Object)
1249          */
1250         @Override
1251         public boolean equals( Object obj ) {
1252             if (obj == this) return true;
1253             if (obj instanceof AttributeTest) {
1254                 AttributeTest that = (AttributeTest)obj;
1255                 return ObjectUtil.isEqualWithNulls(this.typeName, that.typeName)
1256                        && ObjectUtil.isEqualWithNulls(this.attributeNameOrWildcard, that.attributeNameOrWildcard);
1257             }
1258             return false;
1259         }
1260 
1261         /**
1262          * {@inheritDoc}
1263          * 
1264          * @see java.lang.Object#toString()
1265          */
1266         @Override
1267         public String toString() {
1268             return "attribute(" + attributeNameOrWildcard + (typeName != null ? "," + typeName : "") + ")";
1269         }
1270     }
1271 
1272     public static class ElementTest extends KindTest {
1273         private final NameTest elementNameOrWildcard;
1274         private final NameTest typeName;
1275 
1276         public ElementTest( NameTest elementNameOrWildcard,
1277                             NameTest typeName ) {
1278             this.elementNameOrWildcard = elementNameOrWildcard;
1279             this.typeName = typeName;
1280         }
1281 
1282         /**
1283          *@return the element name, which may be a wilcard
1284          */
1285         public NameTest getElementName() {
1286             return elementNameOrWildcard;
1287         }
1288 
1289         /**
1290          * @return typeName
1291          */
1292         public NameTest getTypeName() {
1293             return typeName;
1294         }
1295 
1296         /**
1297          * {@inheritDoc}
1298          * 
1299          * @see java.lang.Object#equals(java.lang.Object)
1300          */
1301         @Override
1302         public boolean equals( Object obj ) {
1303             if (obj == this) return true;
1304             if (obj instanceof ElementTest) {
1305                 ElementTest that = (ElementTest)obj;
1306                 return ObjectUtil.isEqualWithNulls(this.typeName, that.typeName)
1307                        && ObjectUtil.isEqualWithNulls(this.elementNameOrWildcard, that.elementNameOrWildcard);
1308             }
1309             return false;
1310         }
1311 
1312         /**
1313          * {@inheritDoc}
1314          * 
1315          * @see java.lang.Object#toString()
1316          */
1317         @Override
1318         public String toString() {
1319             return "element(" + elementNameOrWildcard + (typeName != null ? "," + typeName : "") + ")";
1320         }
1321     }
1322 
1323     public static class SchemaElementTest extends KindTest {
1324         private final NameTest elementDeclarationName;
1325 
1326         public SchemaElementTest( NameTest elementDeclarationName ) {
1327             this.elementDeclarationName = elementDeclarationName;
1328         }
1329 
1330         /**
1331          *@return the element declaration name, which will be a qualified name
1332          */
1333         public NameTest getElementDeclarationName() {
1334             return elementDeclarationName;
1335         }
1336 
1337         /**
1338          * {@inheritDoc}
1339          * 
1340          * @see java.lang.Object#equals(java.lang.Object)
1341          */
1342         @Override
1343         public boolean equals( Object obj ) {
1344             if (obj == this) return true;
1345             if (obj instanceof SchemaElementTest) {
1346                 SchemaElementTest that = (SchemaElementTest)obj;
1347                 return this.elementDeclarationName.equals(that.elementDeclarationName);
1348             }
1349             return false;
1350         }
1351 
1352         /**
1353          * {@inheritDoc}
1354          * 
1355          * @see java.lang.Object#toString()
1356          */
1357         @Override
1358         public String toString() {
1359             return "schema-element(" + elementDeclarationName + ")";
1360         }
1361     }
1362 
1363     public static class SchemaAttributeTest extends KindTest {
1364         private final NameTest attributeDeclarationName;
1365 
1366         public SchemaAttributeTest( NameTest attributeDeclarationName ) {
1367             this.attributeDeclarationName = attributeDeclarationName;
1368         }
1369 
1370         /**
1371          *@return the attribute declaration name, which will be a qualified name
1372          */
1373         public NameTest getAttributeDeclarationName() {
1374             return attributeDeclarationName;
1375         }
1376 
1377         /**
1378          * {@inheritDoc}
1379          * 
1380          * @see java.lang.Object#equals(java.lang.Object)
1381          */
1382         @Override
1383         public boolean equals( Object obj ) {
1384             if (obj == this) return true;
1385             if (obj instanceof SchemaAttributeTest) {
1386                 SchemaAttributeTest that = (SchemaAttributeTest)obj;
1387                 return this.attributeDeclarationName.equals(that.attributeDeclarationName);
1388             }
1389             return false;
1390         }
1391 
1392         /**
1393          * {@inheritDoc}
1394          * 
1395          * @see java.lang.Object#toString()
1396          */
1397         @Override
1398         public String toString() {
1399             return "schema-attribute(" + attributeDeclarationName + ")";
1400         }
1401     }
1402 
1403     public static class OrderBy extends Component implements Iterable<OrderBySpec> {
1404         private final List<OrderBySpec> orderBySpecifications;
1405 
1406         public OrderBy( List<OrderBySpec> orderBySpecifications ) {
1407             this.orderBySpecifications = orderBySpecifications;
1408         }
1409 
1410         /**
1411          *@return the list of order-by specifications; never null
1412          */
1413         public List<OrderBySpec> getOrderBySpecifications() {
1414             return orderBySpecifications;
1415         }
1416 
1417         /**
1418          * {@inheritDoc}
1419          * 
1420          * @see org.modeshape.jcr.xpath.XPath.Component#collapse()
1421          */
1422         @Override
1423         public Component collapse() {
1424             return super.collapse();
1425         }
1426 
1427         /**
1428          * {@inheritDoc}
1429          * 
1430          * @see java.lang.Iterable#iterator()
1431          */
1432         public Iterator<OrderBySpec> iterator() {
1433             return new ReadOnlyIterator<OrderBySpec>(orderBySpecifications.iterator());
1434         }
1435 
1436         /**
1437          * {@inheritDoc}
1438          * 
1439          * @see java.lang.Object#equals(java.lang.Object)
1440          */
1441         @Override
1442         public boolean equals( Object obj ) {
1443             if (obj == this) return true;
1444             if (obj instanceof OrderBy) {
1445                 OrderBy that = (OrderBy)obj;
1446                 return this.orderBySpecifications.equals(that.orderBySpecifications);
1447             }
1448             return false;
1449         }
1450 
1451         /**
1452          * {@inheritDoc}
1453          * 
1454          * @see java.lang.Object#toString()
1455          */
1456         @Override
1457         public String toString() {
1458             StringBuilder sb = new StringBuilder();
1459             sb.append("order-by(");
1460             boolean first = true;
1461             for (OrderBySpec spec : orderBySpecifications) {
1462                 if (first) first = false;
1463                 else sb.append(',');
1464                 sb.append(spec);
1465             }
1466             sb.append(')');
1467             return sb.toString();
1468         }
1469     }
1470 
1471     public static class OrderBySpec {
1472         private final Order order;
1473         private final FunctionCall scoreFunction;
1474         private final NameTest attributeName;
1475 
1476         public OrderBySpec( Order order,
1477                             FunctionCall scoreFunction ) {
1478             assert order != null;
1479             assert scoreFunction != null;
1480             this.order = order;
1481             this.scoreFunction = scoreFunction;
1482             this.attributeName = null;
1483         }
1484 
1485         public OrderBySpec( Order order,
1486                             NameTest attributeName ) {
1487             assert order != null;
1488             assert attributeName != null;
1489             this.order = order;
1490             this.scoreFunction = null;
1491             this.attributeName = attributeName;
1492         }
1493 
1494         /**
1495          * Get the attribute name for this order specification.
1496          * 
1497          * @return the attribute name, or null if the order is defined by the {@link #getScoreFunction() score function}
1498          */
1499         public NameTest getAttributeName() {
1500             return attributeName;
1501         }
1502 
1503         /**
1504          * Get the score function for this order specification.
1505          * 
1506          * @return the score function with its parameters, or null if the order is defined by the {@link #getAttributeName()
1507          *         attribute name}
1508          */
1509         public FunctionCall getScoreFunction() {
1510             return scoreFunction;
1511         }
1512 
1513         /**
1514          * The order for this specification
1515          * 
1516          * @return the order; never null
1517          */
1518         public Order getOrder() {
1519             return order;
1520         }
1521 
1522         /**
1523          * {@inheritDoc}
1524          * 
1525          * @see java.lang.Object#equals(java.lang.Object)
1526          */
1527         @Override
1528         public boolean equals( Object obj ) {
1529             if (obj == this) return true;
1530             if (obj instanceof OrderBySpec) {
1531                 OrderBySpec that = (OrderBySpec)obj;
1532                 if (this.order != that.order) return false;
1533                 if (this.attributeName != null && !this.attributeName.equals(that.attributeName)) return false;
1534                 if (this.scoreFunction != null && !this.scoreFunction.equals(that.scoreFunction)) return false;
1535                 return true;
1536             }
1537             return false;
1538         }
1539 
1540         /**
1541          * {@inheritDoc}
1542          * 
1543          * @see java.lang.Object#toString()
1544          */
1545         @Override
1546         public String toString() {
1547             StringBuilder sb = new StringBuilder();
1548             if (scoreFunction != null) {
1549                 sb.append(scoreFunction.toString());
1550             } else {
1551                 sb.append('@').append(attributeName.toString());
1552             }
1553             switch (order) {
1554                 case ASCENDING:
1555                     sb.append(" ascending");
1556                     break;
1557                 case DESCENDING:
1558                     sb.append(" descending");
1559                     break;
1560             }
1561             return sb.toString();
1562         }
1563     }
1564 }