1 22 23 package cypress; 24 25 import java.io.*; 26 import java.net.*; 27 import java.util.*; 28 import cypress.spi.*; 29 30 public class CssParser 31 { 32 protected ConditionFactory _conditionFactory; 33 34 37 protected int _current; 38 39 protected DocumentHandler _documentHandler; 40 41 protected String _documentUri; 42 protected ErrorHandler _errorHandler; 43 46 protected CssLexer _lexer; 47 48 protected SelectorFactory _selectorFactory; 49 50 public void setConditionFactory( ConditionFactory factory ) 51 { 52 _conditionFactory = factory; 53 } 54 55 public void setDocumentHandler( DocumentHandler handler ) 56 { 57 _documentHandler = handler; 58 } 59 60 public void setErrorHandler( ErrorHandler handler ) 61 { 62 _errorHandler = handler; 63 } 64 65 public void setSelectorFactory( SelectorFactory factory ) 66 { 67 _selectorFactory = factory; 68 } 69 70 71 public String formatMessage( String key, Object [] args ) 72 { 73 return key; 74 } 75 76 public CssValue parsePropertyValue( InputSource source ) 77 throws CssParseException, IOException 78 { 79 _lexer = new CssLexer( characterStream( source ) ); 80 return parsePropertyValueInternal(); 81 } 82 83 public CssValue parsePropertyValue( String source ) 84 throws CssParseException, IOException 85 { 86 _lexer = new CssLexer( source ); 87 return parsePropertyValueInternal(); 88 } 89 90 public void parseRule( InputSource source ) 91 throws CssParseException, IOException 92 { 93 _lexer = new CssLexer( characterStream( source ) ); 94 parseRuleInternal(); 95 } 96 97 public void parseRule( String source ) 98 throws CssParseException, IOException 99 { 100 _lexer = new CssLexer( source ); 101 parseRuleInternal(); 102 } 103 104 public List parseSelectors( InputSource source ) 105 throws CssParseException, IOException 106 { 107 _lexer = new CssLexer( characterStream( source ) ); 108 return parseSelectorsInternal(); 109 } 110 111 public List parseSelectors( String source ) 112 throws CssParseException, IOException 113 { 114 _lexer = new CssLexer( source ); 115 return parseSelectorsInternal(); 116 } 117 118 public void parseStyleDeclaration( InputSource source ) 119 throws CssParseException, IOException 120 { 121 _lexer = new CssLexer( characterStream( source ) ); 122 parseStyleDeclarationInternal(); 123 } 124 125 public void parseStyleDeclaration( String source ) 126 throws CssParseException, IOException 127 { 128 _lexer = new CssLexer( source ); 129 parseStyleDeclarationInternal(); 130 } 131 132 public void parseStyleSheet( InputSource source ) 133 throws CssParseException, IOException 134 { 135 _lexer = new CssLexer( characterStream( source ) ); 136 137 try 138 { 139 _documentHandler.startDocument( source ); 140 141 _current = _lexer.next(); 142 switch ( _current ) 143 { 144 case Css.Token.COMMENT: 145 _documentHandler.comment( _lexer.getStringValue() ); 146 } 147 148 skipSpacesAndCDOCDC(); 149 150 loop : 151 for( ; ; ) 152 { 153 switch ( _current ) 154 { 155 case Css.Token.EOF: 156 break loop; 157 default: 158 parseRuleSet(); 159 } 160 skipSpacesAndCDOCDC(); 161 } 162 } 163 finally 164 { 165 _documentHandler.endDocument( source ); 166 _lexer = null; 167 } 168 } 169 170 171 public void parseStyleSheet( String uri ) 172 throws CssParseException, IOException 173 { 174 parseStyleSheet( new InputSource( uri ) ); 175 } 176 177 180 protected Reader characterStream( InputSource source ) throws CssParseException 181 { 182 Reader r = source.getCharacterStream(); 183 if( r == null ) 184 { 185 InputStream is = source.getByteStream(); 186 if( is != null ) 187 { 188 r = characterStream( source, is ); 189 } 190 else 191 { 192 String uri = source.getUri(); 193 if( uri != null ) 194 { 195 try 196 { 197 URL url = new URL( uri ); 198 is = url.openStream(); 199 r = characterStream( source, is ); 200 } 201 catch( IOException e ) 202 { 203 throw new CssParseException( e ); 204 } 205 } 206 else 207 { 208 throw new CssParseException( formatMessage( "empty.source", null ) ); 209 } 210 } 211 } 212 return r; 213 } 214 215 218 protected Reader characterStream( InputSource source, InputStream is ) 219 { 220 _documentUri = source.getUri(); 221 if( _documentUri == null ) 222 _documentUri = ""; 223 224 return new InputStreamReader( is ); 225 } 226 227 protected CssParseException createCssParseException( String key ) 228 { 229 return createCssParseException( key, null ); 230 } 231 232 protected CssParseException createCssParseException( String key, Object [] params ) 233 { 234 240 return new CssParseException( formatMessage( key, params ), 241 _lexer.getLine(), 242 _lexer.getColumn() ); 243 } 244 245 248 protected CssValue dimension( boolean positive, CssValue prev ) throws CssParseException 249 { 250 try 251 { 252 float sgn = ( positive ) ? 1 : -1; 253 String val = _lexer.getStringValue(); 254 int i; 255 loop : 256 for( i = 0; i < val.length(); i++ ) 257 { 258 switch ( val.charAt( i ) ) 259 { 260 default: 261 break loop; 262 case '0': 263 case '1': 264 case '2': 265 case '3': 266 case '4': 267 case '5': 268 case '6': 269 case '7': 270 case '8': 271 case '9': 272 case '.': 273 } 274 } 275 nextIgnoreSpaces(); 276 return CssDimension.createDimension( 277 ( sgn * Float.parseFloat( val.substring( 0, i ) ) ), 278 val.substring( i ), 279 prev ); 280 } 281 catch( NumberFormatException e ) 282 { 283 throw createCssParseException( "number.format" ); 284 } 285 } 286 287 290 protected CssValue hexcolor( CssValue prev ) throws CssParseException 291 { 292 String val = _lexer.getStringValue(); 293 int len = val.length(); 294 CssValue params = null; 295 switch ( len ) 296 { 297 case 3: 298 char rc = Character.toLowerCase( val.charAt( 0 ) ); 299 char gc = Character.toLowerCase( val.charAt( 1 ) ); 300 char bc = Character.toLowerCase( val.charAt( 2 ) ); 301 if( !CssCharUtils.isHexadecimal( rc ) || 302 !CssCharUtils.isHexadecimal( gc ) || 303 !CssCharUtils.isHexadecimal( bc ) ) 304 { 305 throw createCssParseException( "rgb.color", new Object []{val} ); 306 } 307 int t; 308 int r = t = ( rc >= '0' && rc <= '9' ) ? rc - '0' : rc - 'a' + 10; 309 t <<= 4; 310 r |= t; 311 int g = t = ( gc >= '0' && gc <= '9' ) ? gc - '0' : gc - 'a' + 10; 312 t <<= 4; 313 g |= t; 314 int b = t = ( bc >= '0' && bc <= '9' ) ? bc - '0' : bc - 'a' + 10; 315 t <<= 4; 316 b |= t; 317 params = CssInteger.createInteger( r, null ); 318 CssValue tmp; 319 tmp = CssOperator.createComma( params ); 320 tmp = CssInteger.createInteger( g, tmp ); 321 tmp = CssOperator.createComma( tmp ); 322 tmp = CssInteger.createInteger( b, tmp ); 323 break; 324 case 6: 325 char rc1 = Character.toLowerCase( val.charAt( 0 ) ); 326 char rc2 = Character.toLowerCase( val.charAt( 1 ) ); 327 char gc1 = Character.toLowerCase( val.charAt( 2 ) ); 328 char gc2 = Character.toLowerCase( val.charAt( 3 ) ); 329 char bc1 = Character.toLowerCase( val.charAt( 4 ) ); 330 char bc2 = Character.toLowerCase( val.charAt( 5 ) ); 331 if( !CssCharUtils.isHexadecimal( rc1 ) || 332 !CssCharUtils.isHexadecimal( rc2 ) || 333 !CssCharUtils.isHexadecimal( gc1 ) || 334 !CssCharUtils.isHexadecimal( gc2 ) || 335 !CssCharUtils.isHexadecimal( bc1 ) || 336 !CssCharUtils.isHexadecimal( bc2 ) ) 337 { 338 throw createCssParseException( "rgb.color" ); 339 } 340 r = ( rc1 >= '0' && rc1 <= '9' ) ? rc1 - '0' : rc1 - 'a' + 10; 341 r <<= 4; 342 r |= ( rc2 >= '0' && rc2 <= '9' ) ? rc2 - '0' : rc2 - 'a' + 10; 343 g = ( gc1 >= '0' && gc1 <= '9' ) ? gc1 - '0' : gc1 - 'a' + 10; 344 g <<= 4; 345 g |= ( gc2 >= '0' && gc2 <= '9' ) ? gc2 - '0' : gc2 - 'a' + 10; 346 b = ( bc1 >= '0' && bc1 <= '9' ) ? bc1 - '0' : bc1 - 'a' + 10; 347 b <<= 4; 348 b |= ( bc2 >= '0' && bc2 <= '9' ) ? bc2 - '0' : bc2 - 'a' + 10; 349 params = CssInteger.createInteger( r, null ); 350 tmp = CssOperator.createComma( params ); 351 tmp = CssInteger.createInteger( g, tmp ); 352 tmp = CssOperator.createComma( tmp ); 353 tmp = CssInteger.createInteger( b, tmp ); 354 break; 355 default: 356 throw createCssParseException( "rgb.color", new Object []{val} ); 357 } 358 nextIgnoreSpaces(); 359 return CssBuiltInFunction.createRgb( params, prev ); 360 } 361 362 365 protected int next() 366 { 367 try 368 { 369 for( ; ; ) 370 { 371 _lexer.clearBuffer(); 372 _current = _lexer.next(); 373 if( _current == Css.Token.COMMENT ) 374 _documentHandler.comment( _lexer.getStringValue() ); 375 else 376 break; 377 } 378 return _current; 379 } 380 catch( CssParseException e ) 381 { 382 reportError( e ); 383 return _current; 384 } 385 } 386 387 390 protected int nextIgnoreSpaces() 391 { 392 try 393 { 394 loop : 395 for( ; ; ) 396 { 397 _lexer.clearBuffer(); 398 _current = _lexer.next(); 399 switch ( _current ) 400 { 401 case Css.Token.COMMENT: 402 _documentHandler.comment( _lexer.getStringValue() ); 403 break; 404 default: 405 break loop; 406 case Css.Token.SPACE: 407 } 408 } 409 return _current; 410 } 411 catch( CssParseException e ) 412 { 413 _errorHandler.error( createCssParseException( e.getMessage() ) ); 414 return _current; 415 } 416 } 417 418 421 protected float number( boolean positive ) throws CssParseException 422 { 423 try 424 { 425 float sgn = ( positive ) ? 1 : -1; 426 String val = _lexer.getStringValue(); 427 nextIgnoreSpaces(); 428 return sgn * Float.parseFloat( val ); 429 } 430 catch( NumberFormatException e ) 431 { 432 throw createCssParseException( "number.format" ); 433 } 434 } 435 436 441 protected CssValue parseExpression( boolean param ) throws CssParseException 442 { 443 CssValue result = parseTerm( null ); 444 CssValue curr = result; 445 446 for( ; ; ) 447 { 448 boolean op = false; 449 switch ( _current ) 450 { 451 case Css.Token.COMMA: 452 op = true; 453 curr = CssOperator.createComma( curr ); 454 nextIgnoreSpaces(); 455 break; 456 case Css.Token.DIVIDE: 457 op = true; 458 curr = CssOperator.createSlash( curr ); 459 nextIgnoreSpaces(); 460 } 461 if( param ) 462 { 463 if( _current == Css.Token.RIGHT_BRACE ) 464 { 465 if( op ) 466 throw createCssParseException( "token", new Object []{new Integer ( _current )} ); 467 468 return result; 469 } 470 curr = parseTerm( curr ); 471 } 472 else 473 { 474 switch ( _current ) 475 { 476 case Css.Token.IMPORTANT_SYMBOL: 477 case Css.Token.SEMI_COLON: 478 case Css.Token.RIGHT_CURLY_BRACE: 479 case Css.Token.EOF: 480 if( op ) 481 throw createCssParseException( "token", new Object []{new Integer ( _current )} ); 482 483 return result; 484 default: 485 curr = parseTerm( curr ); 486 } 487 } 488 } 489 } 490 491 494 protected CssValue parseFunction( boolean positive, CssValue prev ) throws CssParseException 495 { 496 String name = _lexer.getStringValue(); 497 nextIgnoreSpaces(); 498 499 CssValue params = parseExpression( true ); 500 501 if( _current != Css.Token.RIGHT_BRACE ) 502 throw createCssParseException( "token", new Object []{new Integer ( _current )} ); 503 504 nextIgnoreSpaces(); 505 506 predefined : 507 switch ( name.charAt( 0 ) ) 508 { 509 case 'r': 510 case 'R': 511 CssValue lu; 512 if( name.equalsIgnoreCase( "rgb" ) ) 513 { 514 lu = params; 515 if( lu == null ) 516 break; 517 518 switch ( lu.getType() ) 519 { 520 default: 521 break predefined; 522 case CssValue.INTEGER: 523 case CssValue.PERCENTAGE: 524 lu = lu.getNext(); 525 } 526 if( lu == null ) 527 break; 528 529 switch ( lu.getType() ) 530 { 531 default: 532 break predefined; 533 case CssValue.OPERATOR_COMMA: 534 lu = lu.getNext(); 535 } 536 if( lu == null ) 537 break; 538 539 switch ( lu.getType() ) 540 { 541 default: 542 break predefined; 543 case CssValue.INTEGER: 544 case CssValue.PERCENTAGE: 545 lu = lu.getNext(); 546 } 547 if( lu == null ) 548 break; 549 550 switch ( lu.getType() ) 551 { 552 default: 553 break predefined; 554 case CssValue.OPERATOR_COMMA: 555 lu = lu.getNext(); 556 } 557 if( lu == null ) 558 break; 559 560 switch ( lu.getType() ) 561 { 562 default: 563 break predefined; 564 case CssValue.INTEGER: 565 case CssValue.PERCENTAGE: 566 lu = lu.getNext(); 567 } 568 if( lu != null ) 569 break; 570 571 return CssBuiltInFunction.createRgb( params, prev ); 572 } 573 } 574 575 return CssUserFunction.createFunction( name, params, prev ); 576 } 577 578 581 protected CssValue parsePropertyValueInternal() 582 throws CssParseException, IOException 583 { 584 nextIgnoreSpaces(); 585 586 CssValue exp = null; 587 588 try 589 { 590 exp = parseExpression( false ); 591 } 592 catch( CssParseException e ) 593 { 594 reportError( e ); 595 throw e; 596 } 597 598 _lexer = null; 599 600 if( _current != Css.Token.EOF ) 601 _errorHandler.fatal( createCssParseException( "eof.expected" ) ); 602 603 return exp; 604 } 605 606 609 protected void parseRule() 610 { 611 parseRuleSet(); 612 } 613 614 617 protected void parseRuleInternal() 618 throws CssParseException, IOException 619 { 620 nextIgnoreSpaces(); 621 parseRule(); 622 _lexer = null; 623 } 624 625 628 protected void parseRuleSet() 629 { 630 List sl = null; 631 632 try 633 { 634 sl = parseSelectorList(); 635 } 636 catch( CssParseException e ) 637 { 638 reportError( e ); 639 return; 640 } 641 642 try 643 { 644 _documentHandler.startSelector( sl ); 645 646 if( _current != Css.Token.LEFT_CURLY_BRACE ) 647 { 648 reportError( "left.curly.brace" ); 649 if( _current == Css.Token.RIGHT_CURLY_BRACE ) 650 nextIgnoreSpaces(); 651 652 } 653 else 654 { 655 nextIgnoreSpaces(); 656 657 try 658 { 659 parseStyleDeclaration( true ); 660 } 661 catch( CssParseException e ) 662 { 663 reportError( e ); 664 } 665 } 666 } 667 finally 668 { 669 _documentHandler.endSelector( sl ); 670 } 671 } 672 673 676 protected Selector parseSelector() throws CssParseException 677 { 678 return parseSimpleSelector(); 679 } 680 681 684 protected List parseSelectorList() throws CssParseException 685 { 686 ArrayList selectors = new ArrayList(); 687 selectors.add( parseSelector() ); 688 689 for( ; ; ) 690 { 691 if( _current != Css.Token.COMMA ) 692 return selectors; 693 694 nextIgnoreSpaces(); 695 selectors.add( parseSelector() ); 696 } 697 } 698 699 702 protected List parseSelectorsInternal() 703 throws CssParseException, IOException 704 { 705 nextIgnoreSpaces(); 706 List ret = parseSelectorList(); 707 _lexer = null; 708 return ret; 709 } 710 711 714 protected Selector parseSimpleSelector() throws CssParseException 715 { 716 Selector result; 717 718 switch ( _current ) 719 { 720 case Css.Token.IDENTIFIER: 721 result = _selectorFactory.createElementSelector( _lexer.getStringValue() ); 722 next(); 723 break; 724 case Css.Token.ANY: 725 next(); 726 default: 728 result = _selectorFactory.createAnySelector(); 729 } 730 731 Condition cond = null; 732 733 switch ( _current ) 734 { 735 case Css.Token.HASH: 736 cond = _conditionFactory.createIdCondition( _lexer.getStringValue() ); 737 next(); 738 break; 739 case Css.Token.DOT: 740 if( next() != Css.Token.IDENTIFIER ) 741 throw createCssParseException( "identifier" ); 742 743 cond = _conditionFactory.createClassCondition( _lexer.getStringValue() ); 744 next(); 745 break; 746 } 747 748 skipSpaces(); 749 750 if( cond != null ) 751 result = _selectorFactory.createConditionalSelector( result, cond ); 752 753 return result; 754 } 755 756 759 protected void parseStyleDeclaration( boolean inSheet ) 760 throws CssParseException 761 { 762 for( ; ; ) 763 { 764 switch ( _current ) 765 { 766 case Css.Token.EOF: 767 if( inSheet ) 768 throw createCssParseException( "eof" ); 769 return; 770 case Css.Token.RIGHT_CURLY_BRACE: 771 if( !inSheet ) 772 throw createCssParseException( "eof.expected" ); 773 nextIgnoreSpaces(); 774 return; 775 case Css.Token.SEMI_COLON: 776 nextIgnoreSpaces(); 777 continue; 778 default: 779 throw createCssParseException( "identifier" ); 780 case Css.Token.IDENTIFIER: 781 } 782 783 String name = _lexer.getStringValue(); 784 785 if( nextIgnoreSpaces() != Css.Token.COLON ) 786 throw createCssParseException( "colon" ); 787 788 nextIgnoreSpaces(); 789 790 CssValue exp = null; 791 792 try 793 { 794 exp = parseExpression( false ); 795 } 796 catch( CssParseException e ) 797 { 798 reportError( e ); 799 } 800 801 if( exp != null ) 802 { 803 boolean important = false; 804 if( _current == Css.Token.IMPORTANT_SYMBOL ) 805 { 806 important = true; 807 nextIgnoreSpaces(); 808 } 809 _documentHandler.property( name, exp, important ); 810 } 811 } 812 } 813 814 817 protected void parseStyleDeclarationInternal() 818 throws CssParseException, IOException 819 { 820 nextIgnoreSpaces(); 821 try 822 { 823 parseStyleDeclaration( false ); 824 } 825 catch( CssParseException e ) 826 { 827 reportError( e ); 828 } 829 finally 830 { 831 _lexer = null; 832 } 833 } 834 835 838 protected CssValue parseTerm( CssValue prev ) throws CssParseException 839 { 840 boolean plus = true; 841 boolean sgn = false; 842 843 switch ( _current ) 844 { 845 case Css.Token.MINUS: 846 plus = false; 847 case Css.Token.PLUS: 848 next(); 849 sgn = true; 850 default: 851 switch ( _current ) 852 { 853 case Css.Token.INTEGER: 854 int s = ( plus ) ? 1 : -1; 855 int val = s * Integer.parseInt( _lexer.getStringValue() ); 856 nextIgnoreSpaces(); 857 return CssInteger.createInteger( val, prev ); 858 case Css.Token.REAL: 859 return CssFloat.createReal( number( plus ), prev ); 860 case Css.Token.PERCENTAGE: 861 return CssFloat.createPercentage( number( plus ), prev ); 862 case Css.Token.PT: 863 return CssFloat.createPoint( number( plus ), prev ); 864 case Css.Token.PC: 865 return CssFloat.createPica( number( plus ), prev ); 866 case Css.Token.PX: 867 return CssFloat.createPixel( number( plus ), prev ); 868 case Css.Token.CM: 869 return CssFloat.createCentimeter( number( plus ), prev ); 870 case Css.Token.MM: 871 return CssFloat.createMillimeter( number( plus ), prev ); 872 case Css.Token.IN: 873 return CssFloat.createInch( number( plus ), prev ); 874 case Css.Token.EM: 875 return CssFloat.createEm( number( plus ), prev ); 876 case Css.Token.EX: 877 return CssFloat.createEx( number( plus ), prev ); 878 case Css.Token.DIMENSION: 879 return dimension( plus, prev ); 880 case Css.Token.FUNCTION: 881 return parseFunction( plus, prev ); 882 } 883 if( sgn ) 884 throw createCssParseException( "token", new Object []{new Integer ( _current )} ); 885 } 886 switch ( _current ) 887 { 888 case Css.Token.STRING: 889 String val = _lexer.getStringValue(); 890 nextIgnoreSpaces(); 891 return CssString.createString( val, prev ); 892 case Css.Token.IDENTIFIER: 893 val = _lexer.getStringValue(); 894 nextIgnoreSpaces(); 895 896 if( val.equalsIgnoreCase( "inherit" ) ) 897 return CssOperator.createInherit( prev ); 898 else 899 return CssString.createIdent( val, prev ); 900 case Css.Token.URI: 901 val = _lexer.getStringValue(); 902 nextIgnoreSpaces(); 903 return CssString.createUri( val, prev ); 904 case Css.Token.HASH: 905 return hexcolor( prev ); 906 default: 907 throw createCssParseException( "token", new Object []{new Integer ( _current )} ); 908 } 909 } 910 911 914 protected void reportError( String key ) 915 { 916 reportError( key, null ); 917 } 918 919 922 protected void reportError( String key, Object [] params ) 923 { 924 reportError( createCssParseException( key, params ) ); 925 } 926 927 930 protected void reportError( CssParseException e ) 931 { 932 _errorHandler.error( e ); 933 934 int cbraces = 1; 935 for( ; ; ) 936 { 937 switch ( _current ) 938 { 939 case Css.Token.EOF: 940 return; 941 case Css.Token.SEMI_COLON: 942 case Css.Token.RIGHT_CURLY_BRACE: 943 if( --cbraces == 0 ) 944 { 945 nextIgnoreSpaces(); 946 return; 947 } 948 case Css.Token.LEFT_CURLY_BRACE: 949 cbraces++; 950 } 951 nextIgnoreSpaces(); 952 } 953 } 954 955 958 protected int skipSpaces() 959 { 960 int lex = _lexer.getType(); 961 while( lex == Css.Token.SPACE ) 962 { 963 lex = next(); 964 } 965 return lex; 966 } 967 968 971 protected int skipSpacesAndCDOCDC() 972 { 973 loop : 974 for( ; ; ) 975 { 976 switch ( _current ) 977 { 978 default: 979 break loop; 980 case Css.Token.COMMENT: 981 case Css.Token.SPACE: 982 case Css.Token.CDO: 983 case Css.Token.CDC: 984 } 985 _lexer.clearBuffer(); 986 next(); 987 } 988 return _current; 989 } 990 } 991 | Popular Tags |