1 /* Copyright 2004 The Apache Software Foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package org.apache.xmlbeans.impl.common;
17
18 import javax.xml.namespace.QName;
19
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.HashMap;
24 import org.apache.xmlbeans.XmlError;
25 import org.apache.xmlbeans.XmlException;
26 import org.apache.xmlbeans.impl.common.XMLChar;
27
28
29 public class XPath
30 {
31 public static class XPathCompileException extends XmlException
32 {
33 XPathCompileException ( XmlError err )
34 {
35 super( err.toString(), null, err );
36 }
37 }
38
39 //
40 //
41 //
42
43 public static class ExecutionContext
44 {
45 public ExecutionContext ( )
46 {
47 _stack = new ArrayList();
48 }
49
50 public static final int HIT = 0x1;
51 public static final int DESCEND = 0x2;
52 public static final int ATTRS = 0x4;
53
54 public final void init ( XPath xpath )
55 {
56 if (_xpath != xpath)
57 {
58 _xpath = xpath;
59
60 _paths = new PathContext [ xpath._selector._paths.length ];
61
62 for ( int i = 0 ; i < _paths.length ; i++ )
63 _paths[ i ] = new PathContext();
64 }
65
66 _stack.clear();
67
68 for ( int i = 0 ; i < _paths.length ; i++ )
69 _paths[ i ].init( xpath._selector._paths[ i ] );
70 }
71
72 public final int start ( )
73 {
74 int result = 0;
75
76 for ( int i = 0 ; i < _paths.length ; i++ )
77 result |= _paths[ i ].start();
78
79 return result;
80 }
81
82 public final int element ( QName name )
83 {
84 assert name != null;
85
86 _stack.add( name );
87
88 int result = 0;
89
90 for ( int i = 0 ; i < _paths.length ; i++ )
91 result |= _paths[ i ].element( name );
92
93 return result;
94 }
95
96 public final boolean attr ( QName name )
97 {
98 boolean hit = false;
99
100 for ( int i = 0 ; i < _paths.length ; i++ )
101 hit = hit | _paths[ i ].attr( name );
102
103 return hit;
104 }
105
106 public final void end ( )
107 {
108 _stack.remove( _stack.size() - 1 );
109
110 for ( int i = 0 ; i < _paths.length ; i++ )
111 _paths[ i ].end();
112 }
113
114 private final class PathContext
115 {
116 PathContext ( )
117 {
118 _prev = new ArrayList();
119 }
120
121 void init ( Step steps )
122 {
123 _curr = steps;
124 _prev.clear();
125 }
126
127 private QName top ( int i )
128 {
129 return (QName) ExecutionContext.this._stack.get( _stack.size() - 1 - i );
130 }
131
132 // goes back to the begining of the sequence since last // wildcard
133 private void backtrack ( )
134 {
135 assert _curr != null;
136
137 if (_curr._hasBacktrack)
138 { // _backtrack seems to be a pointer to the step that follows a // wildcard
139 // ex: for .//b/c/d steps c and d should backtrack to b in case there isn't a match
140 _curr = _curr._backtrack;
141 return;
142 }
143
144 assert !_curr._deep;
145
146 _curr = _curr._prev;
147
148 search: for ( ; !_curr._deep ; _curr = _curr._prev )
149 {
150 int t = 0;
151
152 for ( Step s = _curr ; !s._deep ; s = s._prev )
153 {
154 if (!s.match( top( t++ )))
155 continue search;
156 }
157
158 break;
159 }
160 }
161
162 int start ( )
163 {
164 assert _curr != null;
165 assert _curr._prev == null;
166
167 if (_curr._name != null)
168 return _curr._flags;
169
170 // If the steps consist on only a terminator, then the path can
171 // only be '.'. In this case, we get a hit, but there is
172 // nothing else to match. No need to backtrack.
173
174 _curr = null;
175
176 return HIT;
177 }
178
179 int element ( QName name )
180 {
181 //System.out.println(" Path.element: " + name);
182 _prev.add( _curr );
183
184 if (_curr == null)
185 return 0;
186
187 assert _curr._name != null;
188
189 if (!_curr._attr && _curr.match( name ))
190 {
191 if ((_curr = _curr._next)._name != null)
192 return _curr._flags;
193
194 backtrack();
195
196 //System.out.println(" element - HIT " + _curr._flags);
197 return _curr == null ? HIT : HIT | _curr._flags;
198 }
199
200 for ( ; ; )
201 {
202 backtrack();
203
204 if (_curr == null)
205 return 0;
206
207 if (_curr.match( name ))
208 {
209 _curr = _curr._next;
210 break;
211 }
212
213 if (_curr._deep)
214 break;
215 }
216
217 return _curr._flags;
218 }
219
220 boolean attr ( QName name )
221 {
222 return _curr != null && _curr._attr && _curr.match( name );
223 }
224
225 void end ( )
226 {
227 //System.out.println(" Path.end ");
228 _curr = (Step) _prev.remove( _prev.size() - 1 );
229 }
230
231 private Step _curr;
232 private List _prev;
233 }
234
235 private XPath _xpath;
236 private ArrayList _stack;
237 private PathContext[] _paths;
238 }
239
240 //
241 //
242 //
243
244 public static XPath compileXPath ( String xpath )
245 throws XPathCompileException
246 {
247 return compileXPath( xpath, "$this", null );
248 }
249
250 public static XPath compileXPath ( String xpath, String currentNodeVar )
251 throws XPathCompileException
252 {
253 return compileXPath( xpath, currentNodeVar, null );
254 }
255
256 public static XPath compileXPath ( String xpath, Map namespaces )
257 throws XPathCompileException
258 {
259 return compileXPath( xpath, "$this", namespaces );
260 }
261
262 public static XPath compileXPath (
263 String xpath, String currentNodeVar, Map namespaces )
264 throws XPathCompileException
265 {
266 return
267 new CompilationContext( namespaces, currentNodeVar ).
268 compile( xpath );
269 }
270
271 private static class CompilationContext
272 {
273 CompilationContext ( Map namespaces, String currentNodeVar )
274 {
275 assert
276 _currentNodeVar == null ||
277 _currentNodeVar.startsWith( "$" );
278
279 if (currentNodeVar == null)
280 _currentNodeVar = "$this";
281 else
282 _currentNodeVar = currentNodeVar;
283
284 _namespaces = new HashMap();
285
286 _externalNamespaces =
287 namespaces == null ? new HashMap() : namespaces;
288 }
289
290 XPath compile ( String expr ) throws XPathCompileException
291 {
292 _offset = 0;
293 _line = 1;
294 _column = 1;
295 _expr = expr;
296
297 return tokenizeXPath();
298 }
299
300 int currChar ( )
301 {
302 return currChar( 0 );
303 }
304
305 int currChar ( int offset )
306 {
307 return
308 _offset + offset >= _expr.length()
309 ? -1
310 : _expr.charAt( _offset + offset );
311 }
312
313 void advance ( )
314 {
315 if (_offset < _expr.length())
316 {
317 char ch = _expr.charAt( _offset );
318
319 _offset++;
320 _column++;
321
322 if (ch == '\r' || ch == '\n')
323 {
324 _line++;
325 _column = 1;
326
327 if (_offset + 1 < _expr.length())
328 {
329 char nextCh = _expr.charAt( _offset + 1 );
330
331 if ((nextCh == '\r' || nextCh == '\n') && ch != nextCh)
332 _offset++;
333 }
334 }
335 }
336 }
337
338 void advance ( int count )
339 {
340 assert count >= 0;
341
342 while ( count-- > 0 )
343 advance();
344 }
345
346 boolean isWhitespace ( )
347 {
348 return isWhitespace( 0 );
349 }
350
351 boolean isWhitespace ( int offset )
352 {
353 int ch = currChar( offset );
354 return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
355 }
356
357 boolean isNCNameStart ( )
358 {
359 return
360 currChar() == -1
361 ? false :
362 XMLChar.isNCNameStart( currChar() );
363 }
364
365 boolean isNCName ( )
366 {
367 return
368 currChar() == -1
369 ? false :
370 XMLChar.isNCName( currChar() );
371 }
372
373 boolean startsWith ( String s )
374 {
375 return startsWith( s, 0 );
376 }
377
378 boolean startsWith ( String s, int offset )
379 {
380 if (_offset + offset >= _expr.length())
381 return false;
382
383 return _expr.startsWith( s, _offset + offset );
384 }
385
386 private XPathCompileException newError ( String msg )
387 {
388 XmlError err =
389 XmlError.forLocation(
390 msg, XmlError.SEVERITY_ERROR, null,
391 _line, _column, _offset );
392
393 return new XPathCompileException( err );
394 }
395
396 String lookupPrefix ( String prefix ) throws XPathCompileException
397 {
398 if (_namespaces.containsKey( prefix ))
399 return (String) _namespaces.get( prefix );
400
401 if (_externalNamespaces.containsKey( prefix ))
402 return (String) _externalNamespaces.get( prefix );
403
404 if (prefix.equals( "xml" ))
405 return "http://www.w3.org/XML/1998/namespace";
406
407 if (prefix.equals( "xs" ))
408 return "http://www.w3.org/2001/XMLSchema";
409
410 if (prefix.equals( "xsi" ))
411 return "http://www.w3.org/2001/XMLSchema-instance";
412
413 if (prefix.equals( "fn" ))
414 return "http://www.w3.org/2002/11/xquery-functions";
415
416 if (prefix.equals( "xdt" ))
417 return "http://www.w3.org/2003/11/xpath-datatypes";
418
419 if (prefix.equals( "local" ))
420 return "http://www.w3.org/2003/11/xquery-local-functions";
421
422 throw newError( "Undefined prefix: " + prefix );
423 }
424
425 private boolean parseWhitespace ( ) throws XPathCompileException
426 {
427 boolean sawSpace = false;
428
429 while ( isWhitespace() )
430 {
431 advance();
432 sawSpace = true;
433 }
434
435 return sawSpace;
436 }
437
438 //
439 // Tokenizing will consume whitespace followed by the tokens, separated
440 // by whitespace. The whitespace following the last token is not
441 // consumed.
442 //
443
444 private boolean tokenize ( String s )
445 {
446 assert s.length() > 0;
447
448 int offset = 0;
449
450 while ( isWhitespace( offset ) )
451 offset++;
452
453 if (!startsWith( s, offset ))
454 return false;
455
456 offset += s.length();
457
458 advance( offset );
459
460 return true;
461 }
462
463 private boolean tokenize ( String s1, String s2 )
464 {
465 assert s1.length() > 0;
466 assert s2.length() > 0;
467
468 int offset = 0;
469
470 while ( isWhitespace( offset ) )
471 offset++;
472
473 if (!startsWith( s1, offset ))
474 return false;
475
476 offset += s1.length();
477
478 while ( isWhitespace( offset ) )
479 offset++;
480
481 if (!startsWith( s2, offset ))
482 return false;
483
484 offset += s2.length();
485
486 advance( offset );
487
488 return true;
489 }
490
491 private boolean tokenize ( String s1, String s2, String s3)
492 {
493 assert s1.length() > 0;
494 assert s2.length() > 0;
495 assert s3.length() > 0;
496
497 int offset = 0;
498
499 while ( isWhitespace( offset ) )
500 offset++;
501
502 if (!startsWith( s1, offset ))
503 return false;
504
505 offset += s1.length();
506
507 while ( isWhitespace( offset ) )
508 offset++;
509
510 if (!startsWith( s2, offset ))
511 return false;
512
513 offset += s2.length();
514
515 while ( isWhitespace( offset ) )
516 offset++;
517
518 if (!startsWith( s3, offset ))
519 return false;
520
521 offset += s3.length();
522
523 while ( isWhitespace( offset ) )
524 offset++;
525
526 advance( offset );
527
528 return true;
529 }
530 private boolean tokenize ( String s1, String s2, String s3,String s4) {
531 assert s1.length() > 0;
532 assert s2.length() > 0;
533 assert s3.length() > 0;
534 assert s4.length() > 0;
535
536 int offset = 0;
537
538 while ( isWhitespace( offset ) )
539 offset++;
540
541 if (!startsWith( s1, offset ))
542 return false;
543
544 offset += s1.length();
545
546 while ( isWhitespace( offset ) )
547 offset++;
548
549 if (!startsWith( s2, offset ))
550 return false;
551
552 offset += s2.length();
553
554 while ( isWhitespace( offset ) )
555 offset++;
556
557 if (!startsWith( s3, offset ))
558 return false;
559
560 offset += s3.length();
561
562 while ( isWhitespace( offset ) )
563 offset++;
564
565 if (!startsWith( s4, offset ))
566 return false;
567
568 offset += s4.length();
569
570 advance( offset );
571
572 return true;
573 }
574
575
576 private String tokenizeNCName ( ) throws XPathCompileException
577 {
578 parseWhitespace();
579
580 if (!isNCNameStart())
581 throw newError( "Expected non-colonized name" );
582
583 StringBuffer sb = new StringBuffer();
584
585 sb.append( (char) currChar() );
586
587 for ( advance() ; isNCName() ; advance() )
588 sb.append( (char) currChar() );
589
590 return sb.toString();
591 }
592
593 private QName getAnyQName ( )
594 {
595 return new QName( "", "" );
596 }
597
598 private QName tokenizeQName ( ) throws XPathCompileException
599 {
600 if (tokenize( "*" ))
601 return getAnyQName();
602
603 String ncName = tokenizeNCName();
604
605 if (!tokenize( ":" ))
606 return new QName( lookupPrefix( "" ), ncName );
607
608 return
609 new QName(
610 lookupPrefix( ncName ),
611 tokenize( "*" ) ? "" : tokenizeNCName() );
612 }
613
614 private String tokenizeQuotedUri ( ) throws XPathCompileException
615 {
616 char quote;
617
618 if (tokenize( "\"" ))
619 quote = '"';
620 else if (tokenize( "'" ))
621 quote = '\'';
622 else
623 throw newError( "Expected quote (\" or ')" );
624
625 StringBuffer sb = new StringBuffer();
626
627 for ( ; ; )
628 {
629 if (currChar() == -1)
630 throw newError( "Path terminated in URI literal" );
631
632 if (currChar() == quote)
633 {
634 advance();
635
636 if (currChar() != quote)
637 break;
638 }
639
640 sb.append( (char) currChar() );
641
642 advance();
643 }
644
645 return sb.toString();
646 }
647
648 private Step addStep ( boolean deep, boolean attr, QName name, Step steps )
649 {
650 Step step = new Step( deep, attr, name );
651
652 if (steps == null)
653 return step;
654
655 Step s = steps;
656
657 while ( steps._next != null )
658 steps = steps._next;
659
660 steps._next = step;
661 step._prev = steps;
662
663 return s;
664 }
665
666 private Step tokenizeSteps ( ) throws XPathCompileException
667 {
668 if (tokenize( "/" ))
669 throw newError( "Absolute paths unsupported" );
670
671 boolean deep;
672
673 if (tokenize( "$", _currentNodeVar, "//" ) || tokenize( ".", "//" ))
674 deep = true;
675 else if (tokenize( "$", _currentNodeVar, "/" ) || tokenize( ".", "/" ))
676 deep = false;
677 else if (tokenize( "$", _currentNodeVar ) || tokenize( "." ))
678 return addStep( false, false, null, null );
679 else
680 deep = false;
681
682 Step steps = null;
683
684 // Compile the steps removing /. and mergind //. with the next step
685
686 boolean deepDot = false;
687
688 for ( ; ; )
689 {
690 if (tokenize( "attribute", "::" ) || tokenize( "@" ))
691 {
692 steps = addStep( deep, true, tokenizeQName(), steps );
693 break;
694 }
695
696 QName name;
697
698 if (tokenize( "." ))
699 deepDot = deepDot || deep;
700 else
701 {
702 tokenize( "child", "::" );
703 if ((name = tokenizeQName()) != null)
704 {
705 steps = addStep( deep, false, name, steps );
706 deep = false; // only this step needs to be deep
707 // other folowing steps will be deep only if they are preceded by // wildcard
708 }
709 }
710
711 if (tokenize( "//" ))
712 {
713 deep = true;
714 deepDot = false;
715 }
716 else if (tokenize( "/" ))
717 {
718 if (deepDot)
719 deep = true;
720 }
721 else
722 break;
723 }
724
725 // If there was a //. at the end of th path, then we need to make
726 // two paths, one with * at the end and another with @* at the end.
727
728 if ((_lastDeepDot = deepDot))
729 {
730 _lastDeepDot = true;
731 steps = addStep( true, false, getAnyQName(), steps );
732 }
733
734 // Add sentinal step (_name == null)
735
736 return addStep( false, false, null, steps );
737 }
738
739 private void computeBacktrack ( Step steps )
740 throws XPathCompileException
741 {
742 //
743 // Compute static backtrack information
744 //
745 // Note that I use the fact that _hasBacktrack is initialized to
746 // false and _backtrack to null in the following code.
747 //
748
749 Step s, t;
750
751 for ( s = steps ; s != null ; s = t )
752 {
753 // Compute the segment from [ s, t )
754
755 for ( t = s._next ; t != null && !t._deep ; )
756 t = t._next;
757
758 // If the segment is NOT rooted at //, then the backtrack is
759 // null for the entire segment, including possible attr and/or
760 // sentinal
761
762 if (!s._deep)
763 {
764 for ( Step u = s ; u != t ; u = u._next )
765 u._hasBacktrack = true;
766
767 continue;
768 }
769
770 // Compute the sequence [ s, u ) of length n which contain no
771 // wild steps.
772
773 int n = 0;
774 Step u = s;
775
776 while ( u != t && u._name != null && !u.isWild() && !u._attr )
777 {
778 n++;
779 u = u._next;
780 }
781
782 // Now, apply KMP to [ s, u ) for fast backtracking
783
784 QName [] pattern = new QName [ n + 1 ];
785 int [] kmp = new int [ n + 1 ];
786
787 Step v = s;
788
789 for ( int i = 0 ; i < n ; i++ )
790 {
791 pattern[ i ] = v._name;
792 v = v._next;
793 }
794
795 pattern[ n ] = getAnyQName();
796
797 int i = 0;
798 int j = kmp[ 0 ] = -1;
799
800 while ( i < n )
801 {
802 while ( j > -1 && !pattern[ i ].equals( pattern[ j ] ) )
803 j = kmp[ j ];
804
805 if (pattern[ ++i ].equals( pattern[ ++j ] ))
806 kmp[ i ] = kmp[ j ];
807 else
808 kmp[ i ] = j;
809 }
810
811 i = 0;
812
813 for ( v = s ; v != u ; v = v._next )
814 {
815 v._hasBacktrack = true;
816 v._backtrack = s;
817
818 for ( j = kmp[ i ] ; j > 0 ; j-- )
819 v._backtrack = v._backtrack._next;
820
821 i++;
822 }
823
824 // Compute the success backtrack and stuff it into an attr and
825 // sentinal if they exist for this segment
826
827 v = s;
828
829 if (n > 1)
830 {
831 for ( j = kmp[ n - 1 ] ; j > 0 ; j-- )
832 v = v._next;
833 }
834
835 if (u != t && u._attr)
836 {
837 u._hasBacktrack = true;
838 u._backtrack = v;
839 u = u._next;
840 }
841
842 if (u != t && u._name == null)
843 {
844 u._hasBacktrack = true;
845 u._backtrack = v;
846 }
847
848 // The first part of a deep segment always backtracks to itself
849
850 assert s._deep;
851
852 s._hasBacktrack = true;
853 s._backtrack = s;
854 }
855 }
856
857 private void tokenizePath ( ArrayList paths )
858 throws XPathCompileException
859 {
860 _lastDeepDot = false;
861
862 Step steps = tokenizeSteps();
863
864 computeBacktrack( steps );
865
866 paths.add( steps );
867
868 // If the last path ended in //., that path will match all
869 // elements, here I make a path which matches all attributes.
870
871 if (_lastDeepDot)
872 {
873 _sawDeepDot = true;
874
875 Step s = null;
876
877 for ( Step t = steps ; t != null ; t = t._next )
878 {
879 if (t._next != null && t._next._next == null)
880 s = addStep( t._deep, true, t._name, s );
881 else
882 s = addStep( t._deep, t._attr, t._name, s );
883 }
884
885 computeBacktrack( s );
886
887 paths.add( s );
888 }
889 }
890
891 private Selector tokenizeSelector ( ) throws XPathCompileException
892 {
893 ArrayList paths = new ArrayList();
894
895 tokenizePath( paths );
896
897 while ( tokenize( "|" ) )
898 tokenizePath( paths );
899
900 return new Selector( (Step[]) paths.toArray( new Step [ 0 ] ) );
901 }
902
903 private XPath tokenizeXPath ( ) throws XPathCompileException
904 {
905 for ( ; ; )
906 {
907 if (tokenize( "declare", "namespace" ))
908 {
909 if (!parseWhitespace())
910 throw newError( "Expected prefix after 'declare namespace'" );
911
912 String prefix = tokenizeNCName();
913
914 if (!tokenize( "=" ))
915 throw newError( "Expected '='" );
916
917 String uri = tokenizeQuotedUri();
918
919 if (_namespaces.containsKey( prefix ))
920 {
921 throw newError(
922 "Redefinition of namespace prefix: " + prefix );
923 }
924
925 _namespaces.put( prefix, uri );
926
927 //return these to saxon:? Is it an error to pass external NS
928 //that conflicts? or should we just override it?
929 if (_externalNamespaces.containsKey( prefix ))
930 {
931 throw newError(
932 "Redefinition of namespace prefix: " + prefix );
933 }
934 _externalNamespaces.put( prefix, uri );
935
936 if (! tokenize( ";" ))
937 {
938 // throw newError(
939 // "Namespace declaration must end with ;" );
940 }
941
942 _externalNamespaces.put(_NS_BOUNDARY,new Integer(_offset));
943
944 continue;
945 }
946
947 if (tokenize( "declare","default", "element", "namespace" ))
948 {
949 String uri = tokenizeQuotedUri();
950
951 if (_namespaces.containsKey( "" ))
952 {
953 throw newError(
954 "Redefinition of default element namespace" );
955 }
956
957 _namespaces.put( "", uri );
958
959 //return these to saxon:? Is it an error to pass external NS
960 //that conflicts? or should we just override it?
961 if (_externalNamespaces.containsKey( XPath._DEFAULT_ELT_NS ))
962 {
963 throw newError("Redefinition of default element namespace : ");
964 }
965 _externalNamespaces.put( XPath._DEFAULT_ELT_NS, uri );
966
967 if (! tokenize( ";" ))
968 throw newError("Default Namespace declaration must end with ;" );
969 //the boundary is the last ; in the prolog...
970 _externalNamespaces.put(_NS_BOUNDARY,new Integer(_offset));
971
972 continue;
973 }
974
975 break;
976 }
977
978 // Add the default prefix mapping if it has not been redefined
979
980 if (!_namespaces.containsKey( "" ))
981 _namespaces.put( "", "" );
982
983 Selector selector = tokenizeSelector();
984
985 parseWhitespace();
986
987 if (currChar() != -1)
988 {
989 throw newError(
990 "Unexpected char '" + (char) currChar() + "'" );
991 }
992
993 return new XPath( selector, _sawDeepDot );
994 }
995
996 //split of prolog decls that are not standard XPath syntax
997 //but work in v1
998 private void processNonXpathDecls(){
999
1000 }
1001
1002 private String _expr;
1003
1004 private boolean _sawDeepDot; // Saw one overall
1005 private boolean _lastDeepDot;
1006
1007 private String _currentNodeVar;
1008
1009 // private Map _namespaces;
1010 protected Map _namespaces;
1011 private Map _externalNamespaces;
1012
1013 private int _offset;
1014 private int _line;
1015 private int _column;
1016 }
1017
1018 private static final class Step
1019 {
1020 Step ( boolean deep, boolean attr, QName name )
1021 {
1022 _name = name;
1023
1024 _deep = deep;
1025 _attr = attr;
1026
1027 int flags = 0;
1028
1029 if (_deep || !_attr)
1030 flags |= ExecutionContext.DESCEND;
1031
1032 if (_attr)
1033 flags |= ExecutionContext.ATTRS;
1034
1035 _flags = flags;
1036 }
1037
1038 boolean isWild ( )
1039 {
1040 return _name.getLocalPart().length() == 0;
1041 }
1042
1043 boolean match ( QName name )
1044 {
1045 String local = _name.getLocalPart();
1046 String nameLocal = name.getLocalPart();
1047 String uri;
1048 String nameUri;
1049
1050 int localLength = local.length();
1051 int uriLength;
1052
1053 // match any name to _name when _name is empty ""@""
1054 if (localLength==0)
1055 {
1056 uri = _name.getNamespaceURI();
1057 uriLength = uri.length();
1058
1059 if (uriLength==0)
1060 return true;
1061
1062 return uri.equals(name.getNamespaceURI());
1063 }
1064
1065 if (localLength!=nameLocal.length())
1066 return false;
1067
1068 uri = _name.getNamespaceURI();
1069 nameUri = name.getNamespaceURI();
1070
1071 if (uri.length()!=nameUri.length())
1072 return false;
1073
1074 return local.equals(nameLocal) && uri.equals(nameUri);
1075 }
1076
1077 final boolean _attr;
1078 final boolean _deep;
1079
1080 int _flags;
1081
1082 final QName _name;
1083
1084 Step _next, _prev;
1085
1086 boolean _hasBacktrack;
1087 Step _backtrack;
1088 }
1089
1090 private static final class Selector
1091 {
1092 Selector ( Step[] paths )
1093 {
1094 _paths = paths;
1095 }
1096
1097 final Step[] _paths;
1098 }
1099
1100 //
1101 //
1102 //
1103
1104 private XPath ( Selector selector, boolean sawDeepDot )
1105 {
1106 _selector = selector;
1107 _sawDeepDot = sawDeepDot;
1108 }
1109
1110 public boolean sawDeepDot ( )
1111 {
1112 return _sawDeepDot;
1113 }
1114
1115 public static final String _NS_BOUNDARY = "$xmlbeans!ns_boundary";
1116 public static final String _DEFAULT_ELT_NS = "$xmlbeans!default_uri";
1117 private final Selector _selector;
1118 private final boolean _sawDeepDot;
1119 }