001/* 002 * Copyright (C) Christian Schulte <cs@schulte.it>, 2012-235 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without 006 * modification, are permitted provided that the following conditions 007 * are met: 008 * 009 * o Redistributions of source code must retain the above copyright 010 * notice, this list of conditions and the following disclaimer. 011 * 012 * o Redistributions in binary form must reproduce the above copyright 013 * notice, this list of conditions and the following disclaimer in 014 * the documentation and/or other materials provided with the 015 * distribution. 016 * 017 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 018 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 019 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 020 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, 021 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 022 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 023 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 024 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 025 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 026 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 027 * 028 * $JOMC: JavaTypeName.java 5043 2015-05-27 07:03:39Z schulte $ 029 * 030 */ 031package org.jomc.model; 032 033import java.io.Serializable; 034import java.lang.ref.Reference; 035import java.lang.ref.SoftReference; 036import java.text.MessageFormat; 037import java.text.ParseException; 038import java.text.ParsePosition; 039import java.util.ArrayList; 040import java.util.Collections; 041import java.util.HashMap; 042import java.util.LinkedList; 043import java.util.List; 044import java.util.Locale; 045import java.util.Map; 046import java.util.ResourceBundle; 047 048/** 049 * Data type of a Java type name. 050 * <p> 051 * This class supports parsing of Java type names as specified in the 052 * Java Language Specification - Java SE 7 Edition - Chapters 3.8ff, 6.5 and 18. 053 * </p> 054 * <p> 055 * <i>Please note that this class will move to package {@code org.jomc.util} in JOMC 2.0.</i> 056 * </p> 057 * 058 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 059 * @version $JOMC: JavaTypeName.java 5043 2015-05-27 07:03:39Z schulte $ 060 * @see #parse(java.lang.String) 061 * @see #valueOf(java.lang.String) 062 * @since 1.4 063 */ 064public final class JavaTypeName implements Serializable 065{ 066 067 /** 068 * Data type of an argument of a parameterized Java type name. 069 * 070 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 071 * @version $JOMC: JavaTypeName.java 5043 2015-05-27 07:03:39Z schulte $ 072 * @since 1.4 073 */ 074 public static final class Argument implements Serializable 075 { 076 077 /** 078 * Flag indicating the argument is a wildcard. 079 * 080 * @serial 081 */ 082 private boolean wildcard; 083 084 /** 085 * The wildcard bounds of the argument. 086 * 087 * @serial 088 */ 089 private String wildcardBounds; 090 091 /** 092 * The type name of the argument. 093 * 094 * @serial 095 */ 096 private JavaTypeName typeName; 097 098 /** 099 * Cached string representation. 100 */ 101 private transient String cachedString; 102 103 /** 104 * Serial version UID for backwards compatibility with 1.4.x object streams. 105 */ 106 private static final long serialVersionUID = -6515267147665760819L; 107 108 /** 109 * Create a new {@code Argument} instance. 110 */ 111 private Argument() 112 { 113 super(); 114 } 115 116 /** 117 * Gets a flag indicating the argument is a wildcard argument. 118 * 119 * @return {@code true}, if the argument is a wildcard argument; {@code false}, else. 120 */ 121 public boolean isWildcard() 122 { 123 return this.wildcard; 124 } 125 126 /** 127 * Gets the wildcard bounds of the argument. 128 * 129 * @return The wildcard bounds of the argument or {@code null}. 130 */ 131 public String getWildcardBounds() 132 { 133 return this.wildcardBounds; 134 } 135 136 /** 137 * Gets the type name of the argument. 138 * 139 * @return The type name of the argument or {@code null}, if the argument is a wildcard argument. 140 */ 141 public JavaTypeName getTypeName() 142 { 143 return this.typeName; 144 } 145 146 /** 147 * Creates a string representation of the instance. 148 * 149 * @return A string representation of the instance. 150 */ 151 @Override 152 public String toString() 153 { 154 if ( this.cachedString == null ) 155 { 156 final StringBuilder builder = new StringBuilder( 128 ); 157 158 if ( this.isWildcard() ) 159 { 160 builder.append( "?" ); 161 162 if ( this.getWildcardBounds() != null && this.getTypeName() != null ) 163 { 164 builder.append( " " ).append( this.getWildcardBounds() ).append( " " ). 165 append( this.getTypeName() ); 166 167 } 168 } 169 else 170 { 171 builder.append( this.getTypeName() ); 172 } 173 174 this.cachedString = builder.toString(); 175 } 176 177 return this.cachedString; 178 } 179 180 } 181 182 /** 183 * Java type name of class {@code Boolean}. 184 * 185 * @see Boolean 186 */ 187 public static final JavaTypeName BOOLEAN; 188 189 /** 190 * Java type name of basic type {@code boolean}. 191 * 192 * @see Boolean#TYPE 193 */ 194 public static final JavaTypeName BOOLEAN_TYPE; 195 196 /** 197 * Java type name of class {@code Byte}. 198 * 199 * @see Byte 200 */ 201 public static final JavaTypeName BYTE; 202 203 /** 204 * Java type name of basic type {@code byte}. 205 * 206 * @see Byte#TYPE 207 */ 208 public static final JavaTypeName BYTE_TYPE; 209 210 /** 211 * Java type name of class {@code Character}. 212 * 213 * @see Character 214 */ 215 public static final JavaTypeName CHARACTER; 216 217 /** 218 * Java type name of basic type {@code char}. 219 * 220 * @see Character#TYPE 221 */ 222 public static final JavaTypeName CHARACTER_TYPE; 223 224 /** 225 * Java type name of class {@code Double}. 226 * 227 * @see Double 228 */ 229 public static final JavaTypeName DOUBLE; 230 231 /** 232 * Java type name of basic type {@code double}. 233 * 234 * @see Double#TYPE 235 */ 236 public static final JavaTypeName DOUBLE_TYPE; 237 238 /** 239 * Java type name of class {@code Float}. 240 * 241 * @see Float 242 */ 243 public static final JavaTypeName FLOAT; 244 245 /** 246 * Java type name of basic type {@code float}. 247 * 248 * @see Float#TYPE 249 */ 250 public static final JavaTypeName FLOAT_TYPE; 251 252 /** 253 * Java type name of class {@code Integer}. 254 * 255 * @see Integer 256 */ 257 public static final JavaTypeName INTEGER; 258 259 /** 260 * Java type name of basic type {@code int}. 261 * 262 * @see Integer#TYPE 263 */ 264 public static final JavaTypeName INTEGER_TYPE; 265 266 /** 267 * Java type name of class {@code Long}. 268 * 269 * @see Long 270 */ 271 public static final JavaTypeName LONG; 272 273 /** 274 * Java type name of basic type {@code long}. 275 * 276 * @see Long#TYPE 277 */ 278 public static final JavaTypeName LONG_TYPE; 279 280 /** 281 * Java type name of class {@code Short}. 282 * 283 * @see Short 284 */ 285 public static final JavaTypeName SHORT; 286 287 /** 288 * Java type name of basic type {@code short}. 289 * 290 * @see Short#TYPE 291 */ 292 public static final JavaTypeName SHORT_TYPE; 293 294 /** 295 * The array dimension of the type name. 296 * 297 * @serial 298 */ 299 private int dimension; 300 301 /** 302 * The flag indicating the type name denotes a primitive type. 303 * 304 * @serial 305 */ 306 private boolean primitive; 307 308 /** 309 * The class name of the type name. 310 * 311 * @serial 312 */ 313 private String className; 314 315 /** 316 * The qualified package name of the type name. 317 * 318 * @serial 319 */ 320 private String packageName; 321 322 /** 323 * The qualified name of the type name. 324 * 325 * @serial 326 */ 327 private String qualifiedName; 328 329 /** 330 * The simple name of the type name. 331 * 332 * @serial 333 */ 334 private String simpleName; 335 336 /** 337 * The arguments of the type name. 338 * 339 * @serial 340 */ 341 private volatile List<Argument> arguments; 342 343 /** 344 * Cached string representation. 345 */ 346 private transient String cachedString; 347 348 /** 349 * Cached instances. 350 */ 351 private static volatile Reference<Map<String, JavaTypeName>> cache; 352 353 /** 354 * Mappings of basic type name to class name encoding. 355 */ 356 private static final Map<String, String> CLASSNAME_ENCODINGS = new HashMap<String, String>( 8 ); 357 358 /** 359 * Serial version UID for backwards compatibility with 1.4.x object streams. 360 */ 361 private static final long serialVersionUID = -4258949347035910249L; 362 363 static 364 { 365 CLASSNAME_ENCODINGS.put( "boolean", "Z" ); 366 CLASSNAME_ENCODINGS.put( "byte", "B" ); 367 CLASSNAME_ENCODINGS.put( "char", "C" ); 368 CLASSNAME_ENCODINGS.put( "double", "D" ); 369 CLASSNAME_ENCODINGS.put( "float", "F" ); 370 CLASSNAME_ENCODINGS.put( "int", "I" ); 371 CLASSNAME_ENCODINGS.put( "long", "J" ); 372 CLASSNAME_ENCODINGS.put( "short", "S" ); 373 374 BOOLEAN = JavaTypeName.valueOf( Boolean.class.getName() ); 375 BOOLEAN_TYPE = JavaTypeName.valueOf( Boolean.TYPE.getName() ); 376 BYTE = JavaTypeName.valueOf( Byte.class.getName() ); 377 BYTE_TYPE = JavaTypeName.valueOf( Byte.TYPE.getName() ); 378 CHARACTER = JavaTypeName.valueOf( Character.class.getName() ); 379 CHARACTER_TYPE = JavaTypeName.valueOf( Character.TYPE.getName() ); 380 DOUBLE = JavaTypeName.valueOf( Double.class.getName() ); 381 DOUBLE_TYPE = JavaTypeName.valueOf( Double.TYPE.getName() ); 382 FLOAT = JavaTypeName.valueOf( Float.class.getName() ); 383 FLOAT_TYPE = JavaTypeName.valueOf( Float.TYPE.getName() ); 384 INTEGER = JavaTypeName.valueOf( Integer.class.getName() ); 385 INTEGER_TYPE = JavaTypeName.valueOf( Integer.TYPE.getName() ); 386 LONG = JavaTypeName.valueOf( Long.class.getName() ); 387 LONG_TYPE = JavaTypeName.valueOf( Long.TYPE.getName() ); 388 SHORT = JavaTypeName.valueOf( Short.class.getName() ); 389 SHORT_TYPE = JavaTypeName.valueOf( Short.TYPE.getName() ); 390 } 391 392 /** 393 * Creates a new {@code JavaTypeName} instance. 394 */ 395 private JavaTypeName() 396 { 397 super(); 398 } 399 400 /** 401 * Gets the {@code Class} object of the type using a given class loader. 402 * 403 * @param classLoader The class loader to use for loading the {@code Class} object to return or {@code null}, to 404 * load that {@code Class} object using the platform's bootstrap class loader. 405 * @param initialize Flag indicating initialization to be performed on the loaded {@code Class} object. 406 * 407 * @return The {@code Class} object of the type. 408 * 409 * @throws ClassNotFoundException if the {@code Class} object of the type is not found searching 410 * {@code classLoader}. 411 * 412 * @see Class#forName(java.lang.String, boolean, java.lang.ClassLoader) 413 */ 414 public Class<?> getClass( final ClassLoader classLoader, final boolean initialize ) throws ClassNotFoundException 415 { 416 Class<?> javaClass = null; 417 418 if ( this.isArray() ) 419 { 420 javaClass = Class.forName( this.getClassName(), initialize, classLoader ); 421 } 422 else if ( this.isPrimitive() ) 423 { 424 if ( BOOLEAN_TYPE.equals( this ) ) 425 { 426 javaClass = Boolean.TYPE; 427 } 428 else if ( BYTE_TYPE.equals( this ) ) 429 { 430 javaClass = Byte.TYPE; 431 } 432 else if ( CHARACTER_TYPE.equals( this ) ) 433 { 434 javaClass = Character.TYPE; 435 } 436 else if ( DOUBLE_TYPE.equals( this ) ) 437 { 438 javaClass = Double.TYPE; 439 } 440 else if ( FLOAT_TYPE.equals( this ) ) 441 { 442 javaClass = Float.TYPE; 443 } 444 else if ( INTEGER_TYPE.equals( this ) ) 445 { 446 javaClass = Integer.TYPE; 447 } 448 else if ( LONG_TYPE.equals( this ) ) 449 { 450 javaClass = Long.TYPE; 451 } 452 else if ( SHORT_TYPE.equals( this ) ) 453 { 454 javaClass = Short.TYPE; 455 } 456 else 457 { 458 throw new AssertionError( this ); 459 } 460 } 461 else 462 { 463 javaClass = Class.forName( this.getClassName(), initialize, classLoader ); 464 } 465 466 return javaClass; 467 } 468 469 /** 470 * Gets the arguments of the type name. 471 * 472 * @return An unmodifiable list holding the arguments of the type name. 473 */ 474 public List<Argument> getArguments() 475 { 476 if ( this.arguments == null ) 477 { 478 this.arguments = new ArrayList<Argument>(); 479 } 480 481 return this.arguments; 482 } 483 484 /** 485 * Gets a flag indicating the type name denotes an array type. 486 * 487 * @return {@code true}, if the type name denotes an array type; {@code false}, else. 488 * 489 * @see Class#isArray() 490 */ 491 public boolean isArray() 492 { 493 return this.dimension > 0; 494 } 495 496 /** 497 * Gets a flag indicating the type name denotes a primitive type. 498 * 499 * @return {@code true}, if the type name denotes a primitive type; {@code false}, else. 500 * 501 * @see Class#isPrimitive() 502 */ 503 public boolean isPrimitive() 504 { 505 return this.primitive; 506 } 507 508 /** 509 * Gets a flag indicating the type name denotes a wrapper type of a primitive type. 510 * 511 * @return {@code true}, if the type name denotes a wrapper type of a primitive type; {@code false}, else. 512 */ 513 public boolean isUnboxable() 514 { 515 // The Java Language Specification - Java SE 7 Edition - 5.1.8. Unboxing Conversion 516 return BOOLEAN.equals( this ) 517 || BYTE.equals( this ) 518 || SHORT.equals( this ) 519 || CHARACTER.equals( this ) 520 || INTEGER.equals( this ) 521 || LONG.equals( this ) 522 || FLOAT.equals( this ) 523 || DOUBLE.equals( this ); 524 525 } 526 527 /** 528 * Gets the type name. 529 * 530 * @param qualified {@code true}, to return a qualified name; {@code false}, to return a simple name. 531 * 532 * @return The type name. 533 */ 534 public String getName( final boolean qualified ) 535 { 536 return qualified 537 ? this.toString() 538 : this.getPackageName().length() > 0 539 ? this.toString().substring( this.getPackageName().length() + 1 ) 540 : this.toString(); 541 542 } 543 544 /** 545 * Gets the class name of the type name. 546 * 547 * @return The class name of the type name. 548 * 549 * @see Class#getName() 550 * @see Class#forName(java.lang.String) 551 */ 552 public String getClassName() 553 { 554 return this.className; 555 } 556 557 /** 558 * Gets the fully qualified package name of the type name. 559 * 560 * @return The fully qualified package name of the type name or an empty string, if the type name denotes a type 561 * located in an unnamed package. 562 * 563 * @see #isUnnamedPackage() 564 */ 565 public String getPackageName() 566 { 567 return this.packageName; 568 } 569 570 /** 571 * Gets a flag indicating the type name denotes a type located in an unnamed package. 572 * 573 * @return {@code true}, if the type name denotes a type located in an unnamed package; {@code false}, else. 574 * 575 * @see #getPackageName() 576 */ 577 public boolean isUnnamedPackage() 578 { 579 return this.getPackageName().length() == 0; 580 } 581 582 /** 583 * Gets the fully qualified name of the type name. 584 * 585 * @return The fully qualified name of the type name. 586 */ 587 public String getQualifiedName() 588 { 589 return this.qualifiedName; 590 } 591 592 /** 593 * Gets the simple name of the type name. 594 * 595 * @return The simple name of the type name. 596 */ 597 public String getSimpleName() 598 { 599 return this.simpleName; 600 } 601 602 /** 603 * Gets the type name applying a boxing conversion. 604 * 605 * @return The converted type name or {@code null}, if the instance cannot be converted. 606 * 607 * @see #isArray() 608 * @see #isPrimitive() 609 */ 610 public JavaTypeName getBoxedName() 611 { 612 JavaTypeName boxedName = null; 613 614 // The Java Language Specification - Java SE 7 Edition - 5.1.7. Boxing Conversion 615 if ( BOOLEAN_TYPE.equals( this ) ) 616 { 617 boxedName = BOOLEAN; 618 } 619 else if ( BYTE_TYPE.equals( this ) ) 620 { 621 boxedName = BYTE; 622 } 623 else if ( SHORT_TYPE.equals( this ) ) 624 { 625 boxedName = SHORT; 626 } 627 else if ( CHARACTER_TYPE.equals( this ) ) 628 { 629 boxedName = CHARACTER; 630 } 631 else if ( INTEGER_TYPE.equals( this ) ) 632 { 633 boxedName = INTEGER; 634 } 635 else if ( LONG_TYPE.equals( this ) ) 636 { 637 boxedName = LONG; 638 } 639 else if ( FLOAT_TYPE.equals( this ) ) 640 { 641 boxedName = FLOAT; 642 } 643 else if ( DOUBLE_TYPE.equals( this ) ) 644 { 645 boxedName = DOUBLE; 646 } 647 648 return boxedName; 649 } 650 651 /** 652 * Gets the type name applying an unboxing conversion. 653 * 654 * @return The converted type name or {@code null}, if the instance cannot be converted. 655 * 656 * @see #isUnboxable() 657 */ 658 public JavaTypeName getUnboxedName() 659 { 660 JavaTypeName unboxedName = null; 661 662 // The Java Language Specification - Java SE 7 Edition - 5.1.8. Unboxing Conversion 663 if ( BOOLEAN.equals( this ) ) 664 { 665 unboxedName = BOOLEAN_TYPE; 666 } 667 else if ( BYTE.equals( this ) ) 668 { 669 unboxedName = BYTE_TYPE; 670 } 671 else if ( SHORT.equals( this ) ) 672 { 673 unboxedName = SHORT_TYPE; 674 } 675 else if ( CHARACTER.equals( this ) ) 676 { 677 unboxedName = CHARACTER_TYPE; 678 } 679 else if ( INTEGER.equals( this ) ) 680 { 681 unboxedName = INTEGER_TYPE; 682 } 683 else if ( LONG.equals( this ) ) 684 { 685 unboxedName = LONG_TYPE; 686 } 687 else if ( FLOAT.equals( this ) ) 688 { 689 unboxedName = FLOAT_TYPE; 690 } 691 else if ( DOUBLE.equals( this ) ) 692 { 693 unboxedName = DOUBLE_TYPE; 694 } 695 696 return unboxedName; 697 } 698 699 /** 700 * Creates a string representation of the instance. 701 * 702 * @return A string representation of the instance. 703 */ 704 @Override 705 public String toString() 706 { 707 if ( this.cachedString == null ) 708 { 709 final StringBuilder builder = new StringBuilder( this.getQualifiedName() ); 710 711 if ( !this.getArguments().isEmpty() ) 712 { 713 builder.append( "<" ); 714 715 for ( int i = 0, s0 = this.getArguments().size(); i < s0; i++ ) 716 { 717 builder.append( this.getArguments().get( i ) ).append( ", " ); 718 } 719 720 builder.setLength( builder.length() - 2 ); 721 builder.append( ">" ); 722 } 723 724 if ( this.isArray() ) 725 { 726 final int idx = this.getQualifiedName().length() - this.dimension * "[]".length(); 727 builder.append( builder.substring( idx, this.getQualifiedName().length() ) ); 728 builder.delete( idx, this.getQualifiedName().length() ); 729 } 730 731 this.cachedString = builder.toString(); 732 } 733 734 return this.cachedString; 735 } 736 737 /** 738 * Gets the hash code value of the object. 739 * 740 * @return The hash code value of the object. 741 */ 742 @Override 743 public int hashCode() 744 { 745 return this.toString().hashCode(); 746 } 747 748 /** 749 * Tests whether another object is compile-time equal to this object. 750 * 751 * @param o The object to compare. 752 * 753 * @return {@code true}, if {@code o} denotes the same compile-time type name than the object; {@code false}, else. 754 */ 755 @Override 756 public boolean equals( final Object o ) 757 { 758 boolean equal = o == this; 759 760 if ( !equal && o instanceof JavaTypeName ) 761 { 762 equal = this.toString().equals( o.toString() ); 763 } 764 765 return equal; 766 } 767 768 /** 769 * Tests whether another object is runtime equal to this object. 770 * 771 * @param o The object to compare. 772 * 773 * @return {@code true}, if {@code o} denotes the same runtime type name than the object; {@code false}, else. 774 */ 775 public boolean runtimeEquals( final Object o ) 776 { 777 boolean equal = o == this; 778 779 if ( !equal && o instanceof JavaTypeName ) 780 { 781 final JavaTypeName that = (JavaTypeName) o; 782 equal = this.getClassName().equals( that.getClassName() ); 783 } 784 785 return equal; 786 } 787 788 /** 789 * Parses text from the beginning of the given string to produce a {@code JavaTypeName} instance. 790 * 791 * @param text The text to parse. 792 * 793 * @return A {@code JavaTypeName} instance corresponding to {@code text}. 794 * 795 * @throws NullPointerException if {@code text} is {@code null}. 796 * @throws ParseException if parsing fails. 797 * 798 * @see #valueOf(java.lang.String) 799 */ 800 public static JavaTypeName parse( final String text ) throws ParseException 801 { 802 if ( text == null ) 803 { 804 throw new NullPointerException( "text" ); 805 } 806 807 return parse( text, false ); 808 } 809 810 /** 811 * Parses text from the beginning of the given string to produce a {@code JavaTypeName} instance. 812 * <p> 813 * Unlike the {@link #parse(String)} method, this method throws an {@code IllegalArgumentException} if parsing 814 * fails. 815 * </p> 816 * 817 * @param text The text to parse. 818 * 819 * @return A {@code JavaTypeName} instance corresponding to {@code text}. 820 * 821 * @throws NullPointerException if {@code text} is {@code null}. 822 * @throws IllegalArgumentException if parsing fails. 823 * 824 * @see #parse(java.lang.String) 825 */ 826 public static JavaTypeName valueOf( final String text ) throws IllegalArgumentException 827 { 828 if ( text == null ) 829 { 830 throw new NullPointerException( "text" ); 831 } 832 833 try 834 { 835 return parse( text, true ); 836 } 837 catch ( final ParseException e ) 838 { 839 throw new AssertionError( e ); 840 } 841 } 842 843 private static JavaTypeName parse( final String text, boolean runtimeException ) throws ParseException 844 { 845 Map<String, JavaTypeName> map = cache == null ? null : cache.get(); 846 847 if ( map == null ) 848 { 849 map = new HashMap<String, JavaTypeName>( 128 ); 850 cache = new SoftReference<Map<String, JavaTypeName>>( map ); 851 } 852 853 synchronized ( map ) 854 { 855 JavaTypeName javaType = map.get( text ); 856 857 if ( javaType == null ) 858 { 859 javaType = new JavaTypeName(); 860 parseType( javaType, text, runtimeException ); 861 862 javaType.arguments = javaType.arguments != null 863 ? Collections.unmodifiableList( javaType.arguments ) 864 : Collections.<Argument>emptyList(); 865 866 final String name = javaType.getName( true ); 867 final JavaTypeName existingInstance = map.get( name ); 868 869 if ( existingInstance != null ) 870 { 871 map.put( text, existingInstance ); 872 javaType = existingInstance; 873 } 874 else 875 { 876 map.put( text, javaType ); 877 map.put( name, javaType ); 878 } 879 } 880 881 return javaType; 882 } 883 } 884 885 /** 886 * JLS - Java SE 7 Edition - Chapter 18. Syntax 887 * <pre> 888 * Type: 889 * BasicType {[]} 890 * ReferenceType {[]} 891 * </pre> 892 * 893 * @see #parseReferenceType(org.jomc.model.JavaTypeName.Tokenizer, org.jomc.model.JavaTypeName, boolean, boolean) 894 */ 895 private static void parseType( final JavaTypeName t, final String text, final boolean runtimeException ) 896 throws ParseException 897 { 898 final Tokenizer tokenizer = new Tokenizer( text, runtimeException ); 899 boolean basic_type_or_reference_type_seen = false; 900 boolean lpar_seen = false; 901 Token token; 902 903 while ( ( token = tokenizer.next() ) != null ) 904 { 905 switch ( token.getKind() ) 906 { 907 case Tokenizer.TK_BASIC_TYPE: 908 if ( basic_type_or_reference_type_seen || !CLASSNAME_ENCODINGS.containsKey( token.getValue() ) ) 909 { 910 if ( runtimeException ) 911 { 912 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 913 } 914 else 915 { 916 throw createInvalidTokenParseException( tokenizer.input(), token ); 917 } 918 } 919 basic_type_or_reference_type_seen = true; 920 t.className = token.getValue(); 921 t.qualifiedName = token.getValue(); 922 t.simpleName = token.getValue(); 923 t.packageName = ""; 924 t.primitive = true; 925 break; 926 927 case Tokenizer.TK_IDENTIFIER: 928 if ( basic_type_or_reference_type_seen ) 929 { 930 if ( runtimeException ) 931 { 932 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 933 } 934 else 935 { 936 throw createInvalidTokenParseException( tokenizer.input(), token ); 937 } 938 } 939 basic_type_or_reference_type_seen = true; 940 tokenizer.back(); 941 parseReferenceType( tokenizer, t, false, runtimeException ); 942 break; 943 944 case Tokenizer.TK_LPAR: 945 if ( !basic_type_or_reference_type_seen || lpar_seen ) 946 { 947 if ( runtimeException ) 948 { 949 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 950 } 951 else 952 { 953 throw createInvalidTokenParseException( tokenizer.input(), token ); 954 } 955 } 956 lpar_seen = true; 957 break; 958 959 case Tokenizer.TK_RPAR: 960 if ( !( basic_type_or_reference_type_seen && lpar_seen ) ) 961 { 962 if ( runtimeException ) 963 { 964 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 965 } 966 else 967 { 968 throw createInvalidTokenParseException( tokenizer.input(), token ); 969 } 970 } 971 lpar_seen = false; 972 t.dimension++; 973 t.className = "[" + t.className; 974 t.qualifiedName += "[]"; 975 t.simpleName += "[]"; 976 break; 977 978 default: 979 if ( runtimeException ) 980 { 981 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 982 } 983 else 984 { 985 throw createInvalidTokenParseException( tokenizer.input(), token ); 986 } 987 988 } 989 } 990 991 if ( !basic_type_or_reference_type_seen || lpar_seen ) 992 { 993 if ( runtimeException ) 994 { 995 throw createUnexpectedEndOfInputIllegalArgumentException( tokenizer.input(), tokenizer.length() ); 996 } 997 else 998 { 999 throw createUnexpectedEndOfInputParseException( tokenizer.input(), tokenizer.length() ); 1000 } 1001 } 1002 1003 if ( t.dimension > 0 ) 1004 { 1005 if ( t.primitive ) 1006 { 1007 t.className = new StringBuilder( t.className.length() ). 1008 append( t.className.substring( 0, t.dimension ) ). 1009 append( CLASSNAME_ENCODINGS.get( t.className.substring( t.dimension ) ) ).toString(); 1010 1011 } 1012 else 1013 { 1014 t.className = new StringBuilder( t.className.length() ). 1015 append( t.className.substring( 0, t.dimension ) ). 1016 append( "L" ).append( t.className.substring( t.dimension ) ).append( ";" ).toString(); 1017 1018 } 1019 } 1020 1021 t.arguments = Collections.unmodifiableList( t.getArguments() ); 1022 } 1023 1024 /** 1025 * JLS - Java SE 7 Edition - Chapter 18. Syntax 1026 * <pre> 1027 * ReferenceType: 1028 * Identifier [TypeArguments] { . Identifier [TypeArguments] } 1029 * </pre> 1030 * 1031 * @see #parseTypeArguments(org.jomc.model.JavaTypeName.Tokenizer, org.jomc.model.JavaTypeName, boolean) 1032 */ 1033 private static void parseReferenceType( final Tokenizer tokenizer, final JavaTypeName t, 1034 final boolean in_type_arguments, final boolean runtimeException ) 1035 throws ParseException 1036 { 1037 final StringBuilder classNameBuilder = new StringBuilder( tokenizer.input().length() ); 1038 final StringBuilder typeNameBuilder = new StringBuilder( tokenizer.input().length() ); 1039 boolean identifier_seen = false; 1040 boolean type_arguments_seen = false; 1041 Token token; 1042 1043 while ( ( token = tokenizer.next() ) != null ) 1044 { 1045 switch ( token.getKind() ) 1046 { 1047 case Tokenizer.TK_IDENTIFIER: 1048 if ( identifier_seen || type_arguments_seen ) 1049 { 1050 if ( runtimeException ) 1051 { 1052 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1053 } 1054 else 1055 { 1056 throw createInvalidTokenParseException( tokenizer.input(), token ); 1057 } 1058 } 1059 identifier_seen = true; 1060 type_arguments_seen = false; 1061 t.simpleName = token.getValue(); 1062 t.packageName = typeNameBuilder.length() > 0 1063 ? typeNameBuilder.substring( 0, typeNameBuilder.length() - 1 ) 1064 : ""; 1065 1066 classNameBuilder.append( token.getValue() ); 1067 typeNameBuilder.append( token.getValue() ); 1068 break; 1069 1070 case Tokenizer.TK_DOT: 1071 if ( !( identifier_seen || type_arguments_seen ) ) 1072 { 1073 if ( runtimeException ) 1074 { 1075 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1076 } 1077 else 1078 { 1079 throw createInvalidTokenParseException( tokenizer.input(), token ); 1080 } 1081 } 1082 identifier_seen = false; 1083 type_arguments_seen = false; 1084 classNameBuilder.append( token.getValue() ); 1085 typeNameBuilder.append( token.getValue() ); 1086 break; 1087 1088 case Tokenizer.TK_LT: 1089 if ( !identifier_seen ) 1090 { 1091 if ( runtimeException ) 1092 { 1093 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1094 } 1095 else 1096 { 1097 throw createInvalidTokenParseException( tokenizer.input(), token ); 1098 } 1099 } 1100 identifier_seen = false; 1101 type_arguments_seen = true; 1102 tokenizer.back(); 1103 parseTypeArguments( tokenizer, t, runtimeException ); 1104 break; 1105 1106 case Tokenizer.TK_LPAR: 1107 if ( !( identifier_seen || type_arguments_seen ) || in_type_arguments ) 1108 { 1109 if ( runtimeException ) 1110 { 1111 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1112 } 1113 else 1114 { 1115 throw createInvalidTokenParseException( tokenizer.input(), token ); 1116 } 1117 } 1118 tokenizer.back(); 1119 t.className = classNameBuilder.toString(); 1120 t.qualifiedName = typeNameBuilder.toString(); 1121 return; 1122 1123 case Tokenizer.TK_COMMA: 1124 case Tokenizer.TK_GT: 1125 if ( !( identifier_seen || type_arguments_seen ) || !in_type_arguments ) 1126 { 1127 if ( runtimeException ) 1128 { 1129 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1130 } 1131 else 1132 { 1133 throw createInvalidTokenParseException( tokenizer.input(), token ); 1134 } 1135 } 1136 tokenizer.back(); 1137 t.className = classNameBuilder.toString(); 1138 t.qualifiedName = typeNameBuilder.toString(); 1139 return; 1140 1141 default: 1142 if ( runtimeException ) 1143 { 1144 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1145 } 1146 else 1147 { 1148 throw createInvalidTokenParseException( tokenizer.input(), token ); 1149 } 1150 1151 } 1152 } 1153 1154 if ( !( identifier_seen || type_arguments_seen ) ) 1155 { 1156 if ( runtimeException ) 1157 { 1158 throw createUnexpectedEndOfInputIllegalArgumentException( tokenizer.input(), tokenizer.length() ); 1159 } 1160 else 1161 { 1162 throw createUnexpectedEndOfInputParseException( tokenizer.input(), tokenizer.length() ); 1163 } 1164 } 1165 1166 t.className = classNameBuilder.toString(); 1167 t.qualifiedName = typeNameBuilder.toString(); 1168 } 1169 1170 /** 1171 * JLS - Java SE 7 Edition - Chapter 18. Syntax 1172 * <pre> 1173 * TypeArguments: 1174 * < TypeArgument { , TypeArgument } > 1175 * </pre> 1176 * 1177 * @see #parseTypeArgument(org.jomc.model.JavaTypeName.Tokenizer, org.jomc.model.JavaTypeName, boolean) 1178 */ 1179 private static void parseTypeArguments( final Tokenizer tokenizer, final JavaTypeName t, 1180 final boolean runtimeException ) 1181 throws ParseException 1182 { 1183 boolean lt_seen = false; 1184 boolean argument_seen = false; 1185 Token token; 1186 1187 while ( ( token = tokenizer.next() ) != null ) 1188 { 1189 switch ( token.getKind() ) 1190 { 1191 case Tokenizer.TK_LT: 1192 if ( lt_seen || argument_seen ) 1193 { 1194 if ( runtimeException ) 1195 { 1196 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1197 } 1198 else 1199 { 1200 throw createInvalidTokenParseException( tokenizer.input(), token ); 1201 } 1202 } 1203 lt_seen = true; 1204 argument_seen = false; 1205 break; 1206 1207 case Tokenizer.TK_GT: 1208 if ( !argument_seen ) 1209 { 1210 if ( runtimeException ) 1211 { 1212 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1213 } 1214 else 1215 { 1216 throw createInvalidTokenParseException( tokenizer.input(), token ); 1217 } 1218 } 1219 return; 1220 1221 case Tokenizer.TK_COMMA: 1222 if ( !argument_seen ) 1223 { 1224 if ( runtimeException ) 1225 { 1226 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1227 } 1228 else 1229 { 1230 throw createInvalidTokenParseException( tokenizer.input(), token ); 1231 } 1232 } 1233 argument_seen = false; 1234 break; 1235 1236 case Tokenizer.TK_IDENTIFIER: 1237 if ( !lt_seen || argument_seen ) 1238 { 1239 if ( runtimeException ) 1240 { 1241 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1242 } 1243 else 1244 { 1245 throw createInvalidTokenParseException( tokenizer.input(), token ); 1246 } 1247 } 1248 argument_seen = true; 1249 tokenizer.back(); 1250 parseTypeArgument( tokenizer, t, runtimeException ); 1251 break; 1252 1253 case Tokenizer.TK_QM: 1254 if ( !lt_seen || argument_seen ) 1255 { 1256 if ( runtimeException ) 1257 { 1258 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1259 } 1260 else 1261 { 1262 throw createInvalidTokenParseException( tokenizer.input(), token ); 1263 } 1264 } 1265 argument_seen = true; 1266 tokenizer.back(); 1267 parseTypeArgument( tokenizer, t, runtimeException ); 1268 break; 1269 1270 default: 1271 if ( runtimeException ) 1272 { 1273 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1274 } 1275 else 1276 { 1277 throw createInvalidTokenParseException( tokenizer.input(), token ); 1278 } 1279 1280 } 1281 } 1282 1283 if ( runtimeException ) 1284 { 1285 throw createUnexpectedEndOfInputIllegalArgumentException( tokenizer.input(), tokenizer.length() ); 1286 } 1287 else 1288 { 1289 throw createUnexpectedEndOfInputParseException( tokenizer.input(), tokenizer.length() ); 1290 } 1291 } 1292 1293 /** 1294 * <dl><dt>JLS - Java SE 7 Edition - Chapter 18. Syntax</dt> 1295 * <dd><pre> 1296 * TypeArgument: 1297 * ReferenceType 1298 * ? [ ( extends | super ) ReferenceType ] 1299 * </pre></dd> 1300 * <dt>JLS - Java SE 7 Edition - Chapter 4.5.1. Type Arguments and Wildcards</dt> 1301 * <dd><pre> 1302 * TypeArgument: 1303 * ReferenceType 1304 * Wildcard 1305 * 1306 * Wildcard: 1307 * ? WildcardBounds<i>opt</i> 1308 * 1309 * WildcardBounds: 1310 * extends ReferenceType 1311 * super ReferenceType 1312 * </pre></dd></dl> 1313 */ 1314 private static void parseTypeArgument( final Tokenizer tokenizer, final JavaTypeName t, 1315 final boolean runtimeException ) 1316 throws ParseException 1317 { 1318 boolean qm_seen = false; 1319 boolean keyword_seen = false; 1320 Token token; 1321 1322 final Argument argument = new Argument(); 1323 t.getArguments().add( argument ); 1324 1325 while ( ( token = tokenizer.next() ) != null ) 1326 { 1327 switch ( token.getKind() ) 1328 { 1329 case Tokenizer.TK_IDENTIFIER: 1330 if ( qm_seen && !keyword_seen ) 1331 { 1332 if ( runtimeException ) 1333 { 1334 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1335 } 1336 else 1337 { 1338 throw createInvalidTokenParseException( tokenizer.input(), token ); 1339 } 1340 } 1341 tokenizer.back(); 1342 argument.typeName = new JavaTypeName(); 1343 parseReferenceType( tokenizer, argument.getTypeName(), true, runtimeException ); 1344 return; 1345 1346 case Tokenizer.TK_QM: 1347 if ( qm_seen ) 1348 { 1349 if ( runtimeException ) 1350 { 1351 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1352 } 1353 else 1354 { 1355 throw createInvalidTokenParseException( tokenizer.input(), token ); 1356 } 1357 } 1358 qm_seen = true; 1359 argument.wildcard = true; 1360 break; 1361 1362 case Tokenizer.TK_KEYWORD: 1363 if ( !qm_seen || keyword_seen 1364 || !( "extends".equals( token.getValue() ) || "super".equals( token.getValue() ) ) ) 1365 { 1366 if ( runtimeException ) 1367 { 1368 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1369 } 1370 else 1371 { 1372 throw createInvalidTokenParseException( tokenizer.input(), token ); 1373 } 1374 } 1375 keyword_seen = true; 1376 argument.wildcardBounds = token.getValue(); 1377 break; 1378 1379 case Tokenizer.TK_COMMA: 1380 case Tokenizer.TK_GT: 1381 if ( !qm_seen || keyword_seen ) 1382 { 1383 if ( runtimeException ) 1384 { 1385 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1386 } 1387 else 1388 { 1389 throw createInvalidTokenParseException( tokenizer.input(), token ); 1390 } 1391 } 1392 tokenizer.back(); 1393 return; 1394 1395 default: 1396 if ( runtimeException ) 1397 { 1398 throw createInvalidTokenIllegalArgumentException( tokenizer.input(), token ); 1399 } 1400 else 1401 { 1402 throw createInvalidTokenParseException( tokenizer.input(), token ); 1403 } 1404 1405 } 1406 } 1407 1408 if ( runtimeException ) 1409 { 1410 throw createUnexpectedEndOfInputIllegalArgumentException( tokenizer.input(), tokenizer.length() ); 1411 } 1412 else 1413 { 1414 throw createUnexpectedEndOfInputParseException( tokenizer.input(), tokenizer.length() ); 1415 } 1416 } 1417 1418 private static ParseException createInvalidTokenParseException( final String input, final Token token ) 1419 { 1420 if ( token.getValue().length() > 1 ) 1421 { 1422 return new ParseException( getMessage( "invalidWord", input, token.getValue(), 1423 token.getPosition() ), token.getPosition() ); 1424 1425 } 1426 else 1427 { 1428 return new ParseException( getMessage( "invalidCharacter", input, token.getValue(), 1429 token.getPosition() ), token.getPosition() ); 1430 1431 } 1432 } 1433 1434 private static IllegalArgumentException createInvalidTokenIllegalArgumentException( final String input, 1435 final Token token ) 1436 { 1437 if ( token.getValue().length() > 1 ) 1438 { 1439 return new IllegalArgumentException( getMessage( "invalidWord", input, token.getValue(), 1440 token.getPosition() ) ); 1441 1442 } 1443 else 1444 { 1445 return new IllegalArgumentException( getMessage( "invalidCharacter", input, token.getValue(), 1446 token.getPosition() ) ); 1447 1448 } 1449 } 1450 1451 private static ParseException createUnexpectedEndOfInputParseException( final String input, 1452 final int length ) 1453 { 1454 return new ParseException( getMessage( "unexpectedEndOfInput", input, length ), length ); 1455 } 1456 1457 private static IllegalArgumentException createUnexpectedEndOfInputIllegalArgumentException( final String input, 1458 final int length ) 1459 { 1460 return new IllegalArgumentException( getMessage( "unexpectedEndOfInput", input, length ) ); 1461 } 1462 1463 private static String getMessage( final String key, final Object... args ) 1464 { 1465 return MessageFormat.format( ResourceBundle.getBundle( 1466 JavaTypeName.class.getName().replace( '.', '/' ), Locale.getDefault() ). 1467 getString( key ), args ); 1468 1469 } 1470 1471 private static final class Token 1472 { 1473 1474 private int kind; 1475 1476 private final int position; 1477 1478 private final String value; 1479 1480 private Token( final int kind, final int position, final String value ) 1481 { 1482 super(); 1483 this.kind = kind; 1484 this.position = position; 1485 this.value = value; 1486 } 1487 1488 private int getKind() 1489 { 1490 return this.kind; 1491 } 1492 1493 private int getPosition() 1494 { 1495 return this.position; 1496 } 1497 1498 private String getValue() 1499 { 1500 return this.value; 1501 } 1502 1503 } 1504 1505 private static final class Tokenizer 1506 { 1507 1508 private static final int TK_BASIC_TYPE = 1; 1509 1510 private static final int TK_KEYWORD = 2; 1511 1512 private static final int TK_LITERAL = 3; 1513 1514 private static final int TK_IDENTIFIER = 4; 1515 1516 private static final int TK_LPAR = 5; 1517 1518 private static final int TK_RPAR = 6; 1519 1520 private static final int TK_LT = 7; 1521 1522 private static final int TK_GT = 8; 1523 1524 private static final int TK_COMMA = 9; 1525 1526 private static final int TK_DOT = 10; 1527 1528 private static final int TK_QM = 11; 1529 1530 private final String input; 1531 1532 private int token; 1533 1534 private final List<Token> tokens; 1535 1536 private int length; 1537 1538 private Tokenizer( final String input, final boolean runtimeException ) throws ParseException 1539 { 1540 super(); 1541 this.input = input; 1542 this.token = 0; 1543 this.tokens = tokenize( input, runtimeException ); 1544 1545 if ( !this.tokens.isEmpty() ) 1546 { 1547 final Token last = this.tokens.get( this.tokens.size() - 1 ); 1548 this.length = last.getPosition() + last.getValue().length(); 1549 } 1550 } 1551 1552 private String input() 1553 { 1554 return this.input; 1555 } 1556 1557 private Token next() 1558 { 1559 final int idx = this.token++; 1560 return idx < this.tokens.size() ? this.tokens.get( idx ) : null; 1561 } 1562 1563 private void back() 1564 { 1565 this.token--; 1566 } 1567 1568 private int length() 1569 { 1570 return this.length; 1571 } 1572 1573 private static List<Token> tokenize( final String input, final boolean runtimeException ) 1574 throws ParseException 1575 { 1576 final List<Token> list = new LinkedList<Token>(); 1577 final ParsePosition pos = new ParsePosition( 0 ); 1578 1579 for ( Token t = nextToken( pos, input, runtimeException ); 1580 t != null; 1581 t = nextToken( pos, input, runtimeException ) ) 1582 { 1583 list.add( t ); 1584 } 1585 1586 return Collections.unmodifiableList( list ); 1587 } 1588 1589 private static Token nextToken( final ParsePosition pos, final String str, final boolean runtimeException ) 1590 throws ParseException 1591 { 1592 for ( final int s0 = str.length(); pos.getIndex() < s0; pos.setIndex( pos.getIndex() + 1 ) ) 1593 { 1594 if ( !Character.isWhitespace( str.charAt( pos.getIndex() ) ) ) 1595 { 1596 break; 1597 } 1598 } 1599 1600 int idx = pos.getIndex(); 1601 Token token = null; 1602 1603 if ( idx < str.length() ) 1604 { 1605 // Check separator characters. 1606 switch ( str.charAt( idx ) ) 1607 { 1608 case ',': 1609 token = new Token( TK_COMMA, idx, "," ); 1610 pos.setIndex( idx + 1 ); 1611 break; 1612 case '.': 1613 token = new Token( TK_DOT, idx, "." ); 1614 pos.setIndex( idx + 1 ); 1615 break; 1616 case '<': 1617 token = new Token( TK_LT, idx, "<" ); 1618 pos.setIndex( idx + 1 ); 1619 break; 1620 case '>': 1621 token = new Token( TK_GT, idx, ">" ); 1622 pos.setIndex( idx + 1 ); 1623 break; 1624 case '[': 1625 token = new Token( TK_LPAR, idx, "[" ); 1626 pos.setIndex( idx + 1 ); 1627 break; 1628 case ']': 1629 token = new Token( TK_RPAR, idx, "]" ); 1630 pos.setIndex( idx + 1 ); 1631 break; 1632 case '?': 1633 token = new Token( TK_QM, idx, "?" ); 1634 pos.setIndex( idx + 1 ); 1635 break; 1636 1637 default: 1638 token = null; 1639 1640 } 1641 1642 // Check basic type. 1643 if ( token == null ) 1644 { 1645 for ( final String basicType : JavaLanguage.BASIC_TYPES ) 1646 { 1647 if ( str.substring( idx ).startsWith( basicType ) ) 1648 { 1649 idx += basicType.length(); 1650 1651 if ( idx >= str.length() 1652 || !Character.isJavaIdentifierPart( str.charAt( idx ) ) ) 1653 { 1654 token = new Token( TK_BASIC_TYPE, pos.getIndex(), basicType ); 1655 pos.setIndex( idx ); 1656 break; 1657 } 1658 1659 idx -= basicType.length(); 1660 } 1661 } 1662 } 1663 1664 // Check keyword. 1665 if ( token == null ) 1666 { 1667 for ( final String keyword : JavaLanguage.KEYWORDS ) 1668 { 1669 if ( str.substring( idx ).startsWith( keyword ) ) 1670 { 1671 idx += keyword.length(); 1672 1673 if ( idx >= str.length() 1674 || !Character.isJavaIdentifierPart( str.charAt( idx ) ) ) 1675 { 1676 token = new Token( TK_KEYWORD, pos.getIndex(), keyword ); 1677 pos.setIndex( idx ); 1678 break; 1679 } 1680 1681 idx -= keyword.length(); 1682 } 1683 } 1684 } 1685 1686 // Check boolean literals. 1687 if ( token == null ) 1688 { 1689 for ( final String literal : JavaLanguage.BOOLEAN_LITERALS ) 1690 { 1691 if ( str.substring( idx ).startsWith( literal ) ) 1692 { 1693 idx += literal.length(); 1694 1695 if ( idx >= str.length() 1696 || !Character.isJavaIdentifierPart( str.charAt( idx ) ) ) 1697 { 1698 token = new Token( TK_LITERAL, pos.getIndex(), literal ); 1699 pos.setIndex( idx ); 1700 break; 1701 } 1702 1703 idx -= literal.length(); 1704 } 1705 } 1706 } 1707 1708 // Check null literal. 1709 if ( token == null ) 1710 { 1711 if ( str.substring( idx ).startsWith( JavaLanguage.NULL_LITERAL ) ) 1712 { 1713 idx += JavaLanguage.NULL_LITERAL.length(); 1714 1715 if ( idx >= str.length() 1716 || !Character.isJavaIdentifierPart( str.charAt( idx ) ) ) 1717 { 1718 token = new Token( TK_LITERAL, pos.getIndex(), JavaLanguage.NULL_LITERAL ); 1719 pos.setIndex( idx ); 1720 } 1721 else 1722 { 1723 idx -= JavaLanguage.NULL_LITERAL.length(); 1724 } 1725 } 1726 } 1727 1728 // Check identifier. 1729 if ( token == null ) 1730 { 1731 for ( final int s0 = str.length(); idx < s0; idx++ ) 1732 { 1733 if ( !( idx == pos.getIndex() 1734 ? Character.isJavaIdentifierStart( str.charAt( idx ) ) 1735 : Character.isJavaIdentifierPart( str.charAt( idx ) ) ) ) 1736 { 1737 break; 1738 } 1739 } 1740 1741 if ( idx != pos.getIndex() ) 1742 { 1743 token = new Token( TK_IDENTIFIER, pos.getIndex(), str.substring( pos.getIndex(), idx ) ); 1744 pos.setIndex( idx ); 1745 } 1746 } 1747 1748 if ( token == null ) 1749 { 1750 final Token invalidToken = 1751 new Token( Integer.MIN_VALUE, idx, Character.toString( str.charAt( idx ) ) ); 1752 1753 if ( runtimeException ) 1754 { 1755 throw createInvalidTokenIllegalArgumentException( str, invalidToken ); 1756 } 1757 else 1758 { 1759 throw createInvalidTokenParseException( str, invalidToken ); 1760 } 1761 } 1762 } 1763 1764 return token; 1765 } 1766 1767 } 1768 1769}