EMMA Coverage Report (generated Tue Nov 18 06:49:09 CET 2014)
[all classes][org.jomc.model]

COVERAGE SUMMARY FOR SOURCE FILE [JavaTypeName.java]

nameclass, %method, %block, %line, %
JavaTypeName.java100% (4/4)89%  (55/62)77%  (1707/2221)79%  (380.2/483)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JavaTypeName100% (1/1)79%  (26/33)69%  (1095/1595)72%  (258.2/358)
access$1300 (String, JavaTypeName$Token): IllegalArgumentException 0%   (0/1)0%   (0/4)0%   (0/1)
createInvalidTokenIllegalArgumentException (String, JavaTypeName$Token): Ille... 0%   (0/1)0%   (0/51)0%   (0/3)
createUnexpectedEndOfInputIllegalArgumentException (String, int): IllegalArgu... 0%   (0/1)0%   (0/17)0%   (0/1)
getUnboxedName (): JavaTypeName 0%   (0/1)0%   (0/59)0%   (0/18)
hashCode (): int 0%   (0/1)0%   (0/4)0%   (0/1)
isUnboxable (): boolean 0%   (0/1)0%   (0/36)0%   (0/1)
isUnnamedPackage (): boolean 0%   (0/1)0%   (0/8)0%   (0/1)
getClass (ClassLoader, boolean): Class 100% (1/1)19%  (16/84)22%  (5/23)
getName (boolean): String 100% (1/1)29%  (6/21)28%  (0.3/1)
getBoxedName (): JavaTypeName 100% (1/1)39%  (23/59)39%  (7/18)
parseTypeArguments (JavaTypeName$Tokenizer, JavaTypeName, boolean): void 100% (1/1)60%  (84/141)70%  (31/44)
valueOf (String): JavaTypeName 100% (1/1)65%  (11/17)60%  (3/5)
parseTypeArgument (JavaTypeName$Tokenizer, JavaTypeName, boolean): void 100% (1/1)66%  (101/153)70%  (28/40)
parseReferenceType (JavaTypeName$Tokenizer, JavaTypeName, boolean, boolean): ... 100% (1/1)80%  (183/228)85%  (51.7/61)
parseType (JavaTypeName, String, boolean): void 100% (1/1)88%  (223/254)89%  (47/53)
parse (String, boolean): JavaTypeName 100% (1/1)90%  (78/87)89%  (17/19)
runtimeEquals (Object): boolean 100% (1/1)91%  (21/23)95%  (4.8/5)
<static initializer> 100% (1/1)100% (110/110)100% (26/26)
JavaTypeName (): void 100% (1/1)100% (3/3)100% (2/2)
access$1400 (String, JavaTypeName$Token): ParseException 100% (1/1)100% (4/4)100% (1/1)
createInvalidTokenParseException (String, JavaTypeName$Token): ParseException 100% (1/1)100% (55/55)100% (3/3)
createUnexpectedEndOfInputParseException (String, int): ParseException 100% (1/1)100% (18/18)100% (1/1)
equals (Object): boolean 100% (1/1)100% (20/20)100% (4/4)
getArguments (): List 100% (1/1)100% (11/11)100% (3/3)
getClassName (): String 100% (1/1)100% (3/3)100% (1/1)
getMessage (String, Object []): String 100% (1/1)100% (12/12)100% (1/1)
getPackageName (): String 100% (1/1)100% (3/3)100% (1/1)
getQualifiedName (): String 100% (1/1)100% (3/3)100% (1/1)
getSimpleName (): String 100% (1/1)100% (3/3)100% (1/1)
isArray (): boolean 100% (1/1)100% (7/7)100% (1/1)
isPrimitive (): boolean 100% (1/1)100% (3/3)100% (1/1)
parse (String): JavaTypeName 100% (1/1)100% (11/11)100% (3/3)
toString (): String 100% (1/1)100% (83/83)100% (14/14)
     
class JavaTypeName$Tokenizer100% (1/1)100% (12/12)97%  (500/514)97%  (98/101)
nextToken (ParsePosition, String, boolean): JavaTypeName$Token 100% (1/1)96%  (384/398)96%  (76/79)
JavaTypeName$Tokenizer (String, boolean): void 100% (1/1)100% (36/36)100% (8/8)
JavaTypeName$Tokenizer (String, boolean, JavaTypeName$1): void 100% (1/1)100% (5/5)100% (1/1)
access$100 (JavaTypeName$Tokenizer): JavaTypeName$Token 100% (1/1)100% (3/3)100% (1/1)
access$400 (JavaTypeName$Tokenizer): String 100% (1/1)100% (3/3)100% (1/1)
access$500 (JavaTypeName$Tokenizer): void 100% (1/1)100% (3/3)100% (1/1)
access$600 (JavaTypeName$Tokenizer): int 100% (1/1)100% (3/3)100% (1/1)
back (): void 100% (1/1)100% (7/7)100% (2/2)
input (): String 100% (1/1)100% (3/3)100% (1/1)
length (): int 100% (1/1)100% (3/3)100% (1/1)
next (): JavaTypeName$Token 100% (1/1)100% (21/21)100% (2/2)
tokenize (String, boolean): List 100% (1/1)100% (29/29)100% (7/7)
     
class JavaTypeName$Argument100% (1/1)100% (9/9)100% (76/76)100% (15/15)
JavaTypeName$Argument (): void 100% (1/1)100% (3/3)100% (2/2)
JavaTypeName$Argument (JavaTypeName$1): void 100% (1/1)100% (3/3)100% (1/1)
access$1002 (JavaTypeName$Argument, String): String 100% (1/1)100% (5/5)100% (1/1)
access$802 (JavaTypeName$Argument, JavaTypeName): JavaTypeName 100% (1/1)100% (5/5)100% (1/1)
access$902 (JavaTypeName$Argument, boolean): boolean 100% (1/1)100% (5/5)100% (1/1)
getTypeName (): JavaTypeName 100% (1/1)100% (3/3)100% (1/1)
getWildcardBounds (): String 100% (1/1)100% (3/3)100% (1/1)
isWildcard (): boolean 100% (1/1)100% (3/3)100% (1/1)
toString (): String 100% (1/1)100% (46/46)100% (9/9)
     
class JavaTypeName$Token100% (1/1)100% (8/8)100% (36/36)100% (9/9)
JavaTypeName$Token (int, int, String): void 100% (1/1)100% (12/12)100% (5/5)
JavaTypeName$Token (int, int, String, JavaTypeName$1): void 100% (1/1)100% (6/6)100% (1/1)
access$1100 (JavaTypeName$Token): int 100% (1/1)100% (3/3)100% (1/1)
access$200 (JavaTypeName$Token): int 100% (1/1)100% (3/3)100% (1/1)
access$300 (JavaTypeName$Token): String 100% (1/1)100% (3/3)100% (1/1)
getKind (): int 100% (1/1)100% (3/3)100% (1/1)
getPosition (): int 100% (1/1)100% (3/3)100% (1/1)
getValue (): String 100% (1/1)100% (3/3)100% (1/1)

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

[all classes][org.jomc.model]
EMMA 2.1.5320 (stable) (C) Vladimir Roubtsov