| 1 | /* | 
| 2 |  *   Copyright (C) Christian Schulte, 2012-235 | 
| 3 |  *   All rights reserved. | 
| 4 |  * | 
| 5 |  *   Redistribution and use in source and binary forms, with or without | 
| 6 |  *   modification, are permitted provided that the following conditions | 
| 7 |  *   are met: | 
| 8 |  * | 
| 9 |  *     o Redistributions of source code must retain the above copyright | 
| 10 |  *       notice, this list of conditions and the following disclaimer. | 
| 11 |  * | 
| 12 |  *     o Redistributions in binary form must reproduce the above copyright | 
| 13 |  *       notice, this list of conditions and the following disclaimer in | 
| 14 |  *       the documentation and/or other materials provided with the | 
| 15 |  *       distribution. | 
| 16 |  * | 
| 17 |  *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | 
| 18 |  *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY | 
| 19 |  *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | 
| 20 |  *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, | 
| 21 |  *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
| 22 |  *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
| 23 |  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
| 24 |  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
| 25 |  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
| 26 |  *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
| 27 |  * | 
| 28 |  *   $JOMC: JavaTypeName.java 4694 2012-12-30 02:41:31Z schulte $ | 
| 29 |  * | 
| 30 |  */ | 
| 31 | package org.jomc.model; | 
| 32 |   | 
| 33 | import java.io.Serializable; | 
| 34 | import java.lang.ref.Reference; | 
| 35 | import java.lang.ref.SoftReference; | 
| 36 | import java.text.MessageFormat; | 
| 37 | import java.text.ParseException; | 
| 38 | import java.text.ParsePosition; | 
| 39 | import java.util.ArrayList; | 
| 40 | import java.util.Collections; | 
| 41 | import java.util.HashMap; | 
| 42 | import java.util.LinkedList; | 
| 43 | import java.util.List; | 
| 44 | import java.util.Locale; | 
| 45 | import java.util.Map; | 
| 46 | import java.util.ResourceBundle; | 
| 47 |   | 
| 48 | /** | 
| 49 |  * Data type of a Java type name. | 
| 50 |  * <p>This class supports parsing of Java type names as specified in the | 
| 51 |  * Java Language Specification - Java SE 7 Edition - Chapters 3.8ff, 6.5 and 18.</p> | 
| 52 |  * | 
| 53 |  * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> | 
| 54 |  * @version $JOMC: JavaTypeName.java 4694 2012-12-30 02:41:31Z schulte $ | 
| 55 |  * @see #parse(java.lang.String) | 
| 56 |  * @see #valueOf(java.lang.String) | 
| 57 |  * @since 1.4 | 
| 58 |  */ | 
| 59 | public final class JavaTypeName implements Serializable | 
| 60 | { | 
| 61 |   | 
| 62 |     /** | 
| 63 |      * Data type of an argument of a parameterized Java type name. | 
| 64 |      * | 
| 65 |      * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> | 
| 66 |      * @version $JOMC: JavaTypeName.java 4694 2012-12-30 02:41:31Z schulte $ | 
| 67 |      * @since 1.4 | 
| 68 |      */ | 
| 69 |     public final static class Argument implements Serializable | 
| 70 |     { | 
| 71 |   | 
| 72 |         /** | 
| 73 |          * Flag indicating the argument is a wildcard. | 
| 74 |          * @serial | 
| 75 |          */ | 
| 76 |         private boolean wildcard; | 
| 77 |   | 
| 78 |         /** | 
| 79 |          * The wildcard bounds of the argument. | 
| 80 |          * @serial | 
| 81 |          */ | 
| 82 |         private String wildcardBounds; | 
| 83 |   | 
| 84 |         /** | 
| 85 |          * The type name of the argument. | 
| 86 |          * @serial | 
| 87 |          */ | 
| 88 |         private JavaTypeName typeName; | 
| 89 |   | 
| 90 |         /** Cached string representation. */ | 
| 91 |         private transient String cachedString; | 
| 92 |   | 
| 93 |         /** Serial version UID for backwards compatibility with 1.4.x object streams. */ | 
| 94 |         private static final long serialVersionUID = -6515267147665760819L; | 
| 95 |   | 
| 96 |         /** Create a new {@code Argument} instance. */ | 
| 97 |         private Argument() | 
| 98 |         { | 
| 99 |             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 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 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 | } |