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