1 17 package org.apache.ws.jaxme.xs; 18 19 import java.util.ArrayList ; 20 import java.util.HashSet ; 21 import java.util.Iterator ; 22 import java.util.List ; 23 import java.util.Set ; 24 25 import org.apache.ws.jaxme.xs.parser.impl.LocSAXException; 26 import org.apache.ws.jaxme.xs.xml.XsEField; 27 import org.apache.ws.jaxme.xs.xml.XsESelector; 28 import org.apache.ws.jaxme.xs.xml.XsQName; 29 import org.apache.ws.jaxme.xs.xml.XsTKeybase; 30 import org.xml.sax.Locator ; 31 import org.xml.sax.SAXException ; 32 33 39 public final class XPathMatcher { 40 private static final XSElementOrAttrRef[] NO_MATCHES 41 = new XSElementOrAttrRef[] {}; 42 43 49 private final InternalNode[] _stateMachineRoots; 50 private final Locator _saxLocator; 51 52 private XPathMatcher( Locator locator, InternalNode[] roots ) { 53 _saxLocator = locator; 54 _stateMachineRoots = roots; 55 } 56 57 69 public static XSElementOrAttrRef[][] match( 70 XsTKeybase keybase, 71 XSElement startingNode 72 ) 73 throws SAXException 74 { 75 XsESelector selector = keybase.getSelector(); 76 XsEField[] fields = keybase.getFields(); 77 78 String selectorXPath = selector.getXpath().getToken(); 79 XPathMatcher baseMatcher = XPathMatcher.parse( 80 selector.getLocator(), 81 selectorXPath, 82 true 83 ); 84 85 XSElementOrAttrRef[] baseElements = baseMatcher.match( startingNode ); 86 87 final int numBaseElements = baseElements.length; 88 final int numFields = fields.length; 89 XSElementOrAttrRef[][] results = new XSElementOrAttrRef[numFields][]; 90 91 92 for ( int i=0; i<numFields; i++ ) { 94 XsEField field = fields[i]; 95 String fieldXPath = field.getXpath().getToken(); 96 97 XPathMatcher fieldMatcher = XPathMatcher.parse( 98 field.getLocator(), 99 fieldXPath, 100 false 101 ); 102 103 Set matches = new HashSet (3); 104 105 for ( 109 int baseElementIndex=0; 110 baseElementIndex<numBaseElements; 111 baseElementIndex++ 112 ) { 113 fieldMatcher.match( 114 baseElements[baseElementIndex].getElement(), 115 matches 116 ); 117 } 118 119 int numResults = matches.size(); 120 results[i] = (XSElementOrAttrRef[]) matches.toArray( 121 new XSElementOrAttrRef[numResults] 122 ); 123 } 124 125 return results; 126 } 127 128 129 130 131 138 public static XPathMatcher parse( 139 Locator locator, 140 String xpath, 141 boolean elementsOnly 142 ) 143 throws SAXException 144 { 145 Tokenizer tokenizer = new Tokenizer( locator, xpath ); 146 List automatasList = new ArrayList (3); 147 148 InternalNode automata = createNextAutomata( tokenizer, elementsOnly ); 149 while ( automata != null ) { 150 automatasList.add( automata ); 151 automata = createNextAutomata( tokenizer, elementsOnly ); 152 } 153 154 int numAutomatas = automatasList.size(); 155 InternalNode[] automataRoots = new InternalNode[numAutomatas]; 156 automataRoots = (InternalNode[]) automatasList.toArray( automataRoots ); 157 158 return new XPathMatcher( locator, automataRoots ); 159 } 160 161 165 public XSElementOrAttrRef[] match( XSElement startingNode ) 166 throws SAXException 167 { 168 Set matches = new HashSet (5); 169 170 match( startingNode, matches ); 171 172 int numMatches = matches.size(); 173 if ( numMatches == 0 ) { 174 return NO_MATCHES; 175 } 176 177 XSElementOrAttrRef[] matchesArray = new XSElementOrAttrRef[numMatches]; 178 179 return (XSElementOrAttrRef[]) matches.toArray( matchesArray ); 180 } 181 182 186 public void match(XSElement startingNode, Set matches) throws SAXException { 187 InternalNode[] roots = _stateMachineRoots; 188 int numRoots = roots.length; 189 190 for ( int i=0; i<numRoots; i++ ) { 191 roots[i].match( startingNode, matches ); 192 } 193 } 194 195 196 197 201 202 209 private static InternalNode createNextAutomata( 210 Tokenizer tokenizer, 211 boolean elementsOnly 212 ) 213 throws SAXException 214 { 215 InternalNode rootNode = createAnEntityNode( tokenizer, elementsOnly ); 216 217 if ( rootNode != null ) { 218 appendASeperatorNode( tokenizer, rootNode, elementsOnly ); 219 } 220 221 return rootNode; 222 } 223 224 229 private static InternalNode createAnEntityNode( 230 Tokenizer tokenizer, 231 boolean elementsOnly 232 ) throws SAXException { 233 Token token = tokenizer.next(); 234 235 switch ( token.getTokenCode() ) { 236 case Tokenizer.ELEMENT_TOKEN: 237 return new NamedChildElementNode( 238 token.getNameSpace(), 239 token.getLabel() 240 ); 241 case Tokenizer.ATTR_TOKEN: 242 if ( elementsOnly ) { 243 tokenizer.throwException( 244 "No references to an attribute are allowed here." 245 ); 246 } 247 248 return new StaticAttributeNode(token.getNameSpace(), token.getLabel()); 249 case Tokenizer.THIS_TOKEN: 250 return new ThisNode(); 251 case Tokenizer.ALL_CHILDREN_TOKEN: 252 return new AllChildrenNode( token.getNameSpace() ); 253 case Tokenizer.END_TOKEN: 254 return null; 255 default: 256 tokenizer.throwException( 257 "Read '" + token.getImage() + "' when a reference to either an " 258 + "attribute or an element was expected." 259 ); 260 } 261 262 263 return null; 266 } 267 268 272 private static void appendAnEntityNode( 273 Tokenizer tokenizer, 274 InternalNode previousNode, 275 boolean elementsOnly 276 ) throws SAXException { 277 InternalNode newNode = createAnEntityNode( tokenizer, elementsOnly ); 278 279 if ( newNode != null ) { 281 previousNode.setNextNode( newNode ); 282 appendASeperatorNode( tokenizer, newNode, elementsOnly ); 283 } 284 } 285 286 293 private static void appendASeperatorNode( 294 Tokenizer tokenizer, 295 InternalNode previousNode, 296 boolean elementsOnly 297 ) throws SAXException { 298 Token token = tokenizer.next(); 299 300 switch ( token.getTokenCode() ) { 301 case Tokenizer.SEPARATOR_TOKEN: 302 appendAnEntityNode( tokenizer, previousNode, elementsOnly ); 303 return; 304 case Tokenizer.OR_TOKEN: 305 return; 306 case Tokenizer.END_TOKEN: 307 return; 308 case Tokenizer.ALL_DESCENDANTS_TOKEN: 309 InternalNode newNode = new AllDescendantsNode( token.getNameSpace() ); 310 previousNode.setNextNode( newNode ); 311 appendAnEntityNode( tokenizer, newNode, elementsOnly ); 312 return; 313 default: 314 tokenizer.throwException( 315 "Unexpected string '" + token.getImage() + "' encountered. Expected" 316 + " either | / or nothing." 317 ); 318 } 319 } 320 321 322 323 324 329 330 333 private static final class Tokenizer { 334 public static final int ELEMENT_TOKEN = 0; 335 public static final int THIS_TOKEN = 1; 336 public static final int ATTR_TOKEN = 2; 337 public static final int ALL_CHILDREN_TOKEN = 3; 338 public static final int ALL_DESCENDANTS_TOKEN = 4; 339 public static final int SEPARATOR_TOKEN = 5; 340 public static final int OR_TOKEN = 6; 341 public static final int END_TOKEN = 7; 342 343 public static final CharHandler STARTING_HANDLER =new InitialCharHandler(); 344 public static final CharHandler ATTRIBUTE_HANDLER 345 = new AttributeCharHandler(); 346 public static final CharHandler ELEMENT_HANDLER = new ElementCharHandler(); 347 348 private final String _xpath; 349 private final Locator _saxLocator; 350 351 private int _pos; 352 353 public Tokenizer( Locator locator, String xpath ) { 354 _xpath = xpath; 355 _saxLocator = locator; 356 } 357 358 public boolean hasNext() { 359 return _pos < _xpath.length(); 360 } 361 362 365 public Token next() throws SAXException { 366 TokenizerState context = new TokenizerState(); 367 while ( context.hasNext() ) { 368 CharHandler ch = context.getCharHandler(); 369 370 ch.process( context ); 371 } 372 373 _pos = context.getPos(); 374 375 return context.createToken(); 376 } 377 378 public void throwException( String msg ) throws SAXException { 379 throw new LocSAXException( msg, _saxLocator ); 380 } 381 382 private class TokenizerState { 383 private final int _startPos; 384 385 private int _pos; 386 private int _markedPos; 387 private int _tokenCode = -1; 388 private String _nameSpace; 389 private String _label; 390 391 private CharHandler _charHandler = STARTING_HANDLER; 392 393 public TokenizerState() { 394 _startPos = Tokenizer.this._pos; 395 _pos = Tokenizer.this._pos; 396 _markedPos = Tokenizer.this._pos; 397 } 398 399 public int getPos() { 400 return _pos; 401 } 402 403 public CharHandler getCharHandler() { 404 return _charHandler; 405 } 406 407 public void setCharHandler( CharHandler ch ) { 408 _charHandler = ch; 409 } 410 411 public boolean hasMatch() { 412 return _tokenCode >= 0; 413 } 414 415 public boolean hasNext() { 416 boolean boo = !hasMatch() && this._pos < _xpath.length(); 417 418 return boo; 419 } 420 421 public char peekAhead() { 422 return Tokenizer.this._xpath.charAt( _pos ); 423 } 424 425 public char scrollAhead() { 426 return Tokenizer.this._xpath.charAt( _pos++ ); 427 } 428 429 432 public void skipWhiteSpace() { 433 final String xpath = Tokenizer.this._xpath; 434 final int maxPos = xpath.length(); 435 436 int pos = _pos; 437 while ( pos < maxPos ) { 438 char ch = xpath.charAt( pos ); 439 440 if ( Character.isWhitespace(ch) ) { 441 pos++; 442 } else { 443 break; 444 } 445 } 446 447 _pos = pos; 448 } 449 450 public void skipOverIdentifier() { 451 final String xpath = Tokenizer.this._xpath; 452 final int maxPos = xpath.length(); 453 454 int pos = _pos; 455 while ( pos < maxPos ) { 456 char ch = xpath.charAt( pos ); 457 458 if ( Character.isLetterOrDigit(ch) || ch == '_' || ch == '-' ) { 459 pos++; 460 } else { 461 break; 462 } 463 } 464 465 _pos = pos; 466 } 467 468 public void setTokenCode( int tokenCode ) { 469 _tokenCode = tokenCode; 470 } 471 472 public void markPos() { 473 _markedPos = _pos; 474 } 475 476 public void saveNameSpace() { 477 _nameSpace = Tokenizer.this._xpath.substring( _markedPos, _pos ); 478 _markedPos = _pos; 479 } 480 481 public void saveLabel() { 482 _label = Tokenizer.this._xpath.substring( _markedPos, _pos ); 483 _markedPos = _pos; 484 } 485 486 public Token createToken() { 487 if ( !hasMatch() && !hasNext() ) { 488 _tokenCode = END_TOKEN; 489 } 490 491 return new Token( 492 _tokenCode, 493 _nameSpace, 494 _label, 495 _xpath.substring( _startPos, _pos ) 496 ); 497 } 498 499 public void throwException( String msg ) throws SAXException { 500 Tokenizer.this.throwException( msg ); 501 } 502 } 503 504 512 private static abstract class CharHandler { 513 public abstract void process( TokenizerState context ) 514 throws SAXException; 515 } 516 517 524 private static final class InitialCharHandler extends CharHandler { 525 public void process( TokenizerState context ) 526 throws SAXException 527 { 528 context.skipWhiteSpace(); 530 531 if ( !context.hasNext() ) { 532 return; 533 } 534 535 char ch = context.peekAhead(); 536 switch ( ch ) { 537 case '*': 538 context.scrollAhead(); 539 context.setTokenCode( Tokenizer.ALL_CHILDREN_TOKEN ); 540 break; 541 case '.': 542 context.scrollAhead(); 543 context.setTokenCode( Tokenizer.THIS_TOKEN ); 544 break; 545 case '/': 546 context.scrollAhead(); 547 if ( context.peekAhead() == '/' ) { 548 context.scrollAhead(); 549 context.setTokenCode( Tokenizer.ALL_DESCENDANTS_TOKEN ); 550 } else { 551 context.setTokenCode( Tokenizer.SEPARATOR_TOKEN ); 552 } 553 554 break; 555 case '|': 556 context.scrollAhead(); 557 context.setTokenCode( Tokenizer.OR_TOKEN ); 558 break; 559 case '@': 560 context.scrollAhead(); 561 context.markPos(); 562 context.setCharHandler( ATTRIBUTE_HANDLER ); 563 break; 564 default: 565 context.setCharHandler( ELEMENT_HANDLER ); 566 } 567 } 568 } 569 570 573 private static final class ElementCharHandler extends CharHandler { 574 public void process( TokenizerState context ) 575 throws SAXException 576 { 577 context.skipOverIdentifier(); 578 579 if ( context.hasNext() ) { 580 if ( context.peekAhead() == ':' ) { 581 context.saveNameSpace(); 582 context.scrollAhead(); 583 584 if ( context.hasNext() ) { 585 if ( context.peekAhead() == '*' ) { 586 context.scrollAhead(); 587 context.setTokenCode( Tokenizer.ALL_CHILDREN_TOKEN ); 588 589 return; 590 } 591 } 592 593 context.markPos(); 594 context.skipOverIdentifier(); 595 } 596 } 597 598 context.saveLabel(); 599 context.setTokenCode( Tokenizer.ELEMENT_TOKEN ); 600 } 601 } 602 603 606 private static final class AttributeCharHandler extends CharHandler { 607 public void process( TokenizerState context ) 608 throws SAXException 609 { 610 context.skipOverIdentifier(); 611 612 if ( context.hasNext() && context.peekAhead() == ':' ) { 613 context.saveNameSpace(); 614 context.scrollAhead(); 615 context.markPos(); 616 context.skipOverIdentifier(); 617 } 618 619 context.saveLabel(); 620 context.setTokenCode( Tokenizer.ATTR_TOKEN ); 621 } 622 } 623 } 624 625 629 private static final class Token { 630 private final int _tokenCode; 631 632 636 private final String _nameSpace; 637 638 641 private final String _label; 642 643 646 private final String _image; 647 648 public Token(int tokenCode, String nameSpace, String label, String image) { 649 _tokenCode = tokenCode; 650 _nameSpace = nameSpace; 651 _label = label; 652 _image = image; 653 } 654 655 public int getTokenCode() { 656 return _tokenCode; 657 } 658 659 public String getNameSpace() { 660 return _nameSpace; 661 } 662 663 public String getLabel() { 664 return _label; 665 } 666 667 public String getImage() { 668 return _image; 669 } 670 } 671 672 673 674 680 681 685 private abstract static class InternalNode { 686 private InternalNode _next; 687 688 700 public abstract void match( XSElement currentElement, Set matches ) 701 throws SAXException; 702 703 708 public final void setNextNode( InternalNode next ) { 709 _next = next; 710 } 711 712 717 protected final void continueSearchFor( 718 XSElement currentElement, 719 Set matches 720 ) throws SAXException { 721 InternalNode next = _next; 722 723 if ( next == null ) { 724 matches.add( new XSElementOrAttrRef(currentElement) ); 725 } else { 726 next.match( currentElement, matches ); 727 } 728 } 729 730 734 protected boolean doesMatch(String nameSpace, String name, XsQName qName) { 735 boolean boo = doesNSMatch(nameSpace, qName) 736 && name.equals( qName.getLocalName() ); 737 738 return boo; 739 } 740 741 745 protected boolean doesNSMatch(String nameSpace, XsQName qName) { 746 if ( nameSpace == null ) { 747 return qName.getPrefix() == null; 748 } else { 749 return nameSpace.equals( qName.getPrefix() ); 750 } 751 } 752 753 protected Iterator getChildrenIteratorFor( XSElement element ) 755 throws SAXException 756 { 757 List children = new ArrayList (5); 758 759 XSType type = element.getType(); 760 761 if ( !type.isSimple() ) { 762 XSComplexType complexType = type.getComplexType(); 763 764 if ( !complexType.isEmpty() ) { 765 XSParticle particle = complexType.getParticle(); 766 767 if ( particle.isElement() ) { 768 children.add( particle.getElement() ); 769 } else if ( particle.isGroup() ) { 770 XSGroup group = particle.getGroup(); 771 772 XSParticle[] particles = group.getParticles(); 773 int numParticles = particles.length; 774 775 for ( int i=0; i<numParticles; i++ ) { 776 XSParticle groupedParticle = particles[i]; 777 778 if ( groupedParticle.isElement() ) { 779 children.add( groupedParticle.getElement() ); 780 } 781 } 782 } 783 } 784 } 785 786 return children.iterator(); 787 } 788 } 789 790 793 private static final class ThisNode extends InternalNode { 794 798 public static final ThisNode LEAF_INSTANCE = new ThisNode(); 799 800 public void match( XSElement currentElement, Set matches ) 801 throws SAXException 802 { 803 continueSearchFor( currentElement, matches ); 804 } 805 } 806 807 810 private static final class NamedChildElementNode extends InternalNode { 811 private final String _nameSpace; 812 private final String _name; 813 814 820 public NamedChildElementNode( String nameSpace, String name ) { 821 _nameSpace = nameSpace; 822 _name = name; 823 } 824 825 public void match( XSElement currentElement, Set matches ) 826 throws SAXException 827 { 828 Iterator iterator = getChildrenIteratorFor( currentElement ); 829 830 while ( iterator.hasNext() ) { 831 XSElement element = (XSElement) iterator.next(); 832 833 if ( doesMatch(_nameSpace, _name, element.getName()) ) { 834 continueSearchFor( element, matches ); 835 836 break; 838 } 839 } 840 } 841 } 842 843 846 private static final class StaticAttributeNode extends InternalNode { 847 private final String _nameSpace; 848 private final String _name; 849 850 856 public StaticAttributeNode( String nameSpace, String name ) { 857 _nameSpace = nameSpace; 858 _name = name; 859 } 860 861 public void match( XSElement currentElement, Set matches ) 862 throws SAXException 863 { 864 XSType type = currentElement.getType(); 866 867 if ( !type.isSimple() ) { 868 XSComplexType complexType = type.getComplexType(); 869 XSAttributable[] attributables = complexType.getAttributes(); 870 int numAttribables = attributables.length; 871 872 for ( int i=0; i<numAttribables; i++ ) { 874 XSAttributable attributable = attributables[i]; 875 876 if ( attributable instanceof XSAttribute ) { 877 XSAttribute attribute = (XSAttribute) attributable; 878 879 if ( doesMatch(_nameSpace, _name, attribute.getName()) ) { 880 matches.add( new XSElementOrAttrRef(attribute) ); 881 882 return; 883 } 884 } 885 } 886 } 887 } 888 } 889 890 893 private static final class AllChildrenNode extends InternalNode { 894 private final String _nameSpace; 895 896 public AllChildrenNode( String nameSpace ) { 897 _nameSpace = nameSpace; 898 } 899 900 public void match( XSElement currentElement, Set matches ) 901 throws SAXException 902 { 903 String nameSpace = _nameSpace; 904 Iterator iterator = getChildrenIteratorFor( currentElement ); 905 906 while ( iterator.hasNext() ) { 907 XSElement element = (XSElement) iterator.next(); 908 909 if ( doesNSMatch(nameSpace, element.getName()) ) { 910 continueSearchFor( element, matches ); 911 } 912 } 913 } 914 } 915 916 919 private static final class AllDescendantsNode extends InternalNode { 920 private final String _nameSpace; 921 922 public AllDescendantsNode( String nameSpace ) { 923 _nameSpace = nameSpace; 924 } 925 926 public void match( XSElement currentElement, Set matches ) 927 throws SAXException 928 { 929 String nameSpace = _nameSpace; 930 Iterator iterator = getChildrenIteratorFor( currentElement ); 931 932 while ( iterator.hasNext() ) { 933 XSElement element = (XSElement) iterator.next(); 934 935 if ( doesNSMatch(nameSpace, element.getName()) ) { 936 continueSearchFor( element, matches ); 937 938 this.match( element, matches ); 940 } 941 } 942 } 943 } 944 } 945 | Popular Tags |