public class TypeRecognitionException extends RuntimeException {

    private static final long serialVersionUID = -4337867945837834337L;

    public TypeRecognitionException(String message) {
        super(message);
    }
    
    public TypeRecognitionException(String message, Throwable t) {
        super(message, t);
    }
    
} public class TypeValidationException extends RuntimeException {

    private static final long serialVersionUID = -7104333842847446754L;

    public TypeValidationException(String message) {
        super(message);
    }
    
    public TypeValidationException(String message, Throwable t) {
        super(message, t);
    }
    
} public class TypeLexer extends Lexer {
    public static final int WS=14;
    public static final int UNKNOWN=5;
    public static final int COMMA=8;
    public static final int EXT=11;
    public static final int ENDTYPEPARAM=7;
    public static final int SUPER=10;
    public static final int EOF=-1;
    public static final int EXTENDS=9;
    public static final int ARRAY=4;
    public static final int BEGINTYPEPARAM=6;
    public static final int SUP=12;
    public static final int CLASSNAME=13;

    // delegates
    // delegators

    public TypeLexer() {;} 
    public TypeLexer(CharStream input) {
        this(input, new RecognizerSharedState());
    }
    public TypeLexer(CharStream input, RecognizerSharedState state) {
        super(input,state);

    }
    public String getGrammarFileName() { return "org/javaruntype/type/parser/Type.g"; }

    // $ANTLR start "ARRAY"
    public final void mARRAY() throws RecognitionException {
        try {
            int _type = ARRAY;
            int _channel = DEFAULT_TOKEN_CHANNEL;
            // org/javaruntype/type/parser/Type.g:30:7: ( '[]' )
            // org/javaruntype/type/parser/Type.g:30:9: '[]'
            {
            match("[]"); 


            }

            state.type = _type;
            state.channel = _channel;
        }
        finally {
        }
    }
    // $ANTLR end "ARRAY"

    // $ANTLR start "UNKNOWN"
    public final void mUNKNOWN() throws RecognitionException {
        try {
            int _type = UNKNOWN;
            int _channel = DEFAULT_TOKEN_CHANNEL;
            // org/javaruntype/type/parser/Type.g:31:9: ( '?' )
            // org/javaruntype/type/parser/Type.g:31:11: '?'
            {
            match('?'); 

            }

            state.type = _type;
            state.channel = _channel;
        }
        finally {
        }
    }
    // $ANTLR end "UNKNOWN"

    // $ANTLR start "BEGINTYPEPARAM"
    public final void mBEGINTYPEPARAM() throws RecognitionException {
        try {
            int _type = BEGINTYPEPARAM;
            int _channel = DEFAULT_TOKEN_CHANNEL;
            // org/javaruntype/type/parser/Type.g:32:16: ( '<' )
            // org/javaruntype/type/parser/Type.g:32:18: '<'
            {
            match('<'); 

            }

            state.type = _type;
            state.channel = _channel;
        }
        finally {
        }
    }
    // $ANTLR end "BEGINTYPEPARAM"

    // $ANTLR start "ENDTYPEPARAM"
    public final void mENDTYPEPARAM() throws RecognitionException {
        try {
            int _type = ENDTYPEPARAM;
            int _channel = DEFAULT_TOKEN_CHANNEL;
            // org/javaruntype/type/parser/Type.g:33:14: ( '>' )
            // org/javaruntype/type/parser/Type.g:33:16: '>'
            {
            match('>'); 

            }

            state.type = _type;
            state.channel = _channel;
        }
        finally {
        }
    }
    // $ANTLR end "ENDTYPEPARAM"

    // $ANTLR start "COMMA"
    public final void mCOMMA() throws RecognitionException {
        try {
            int _type = COMMA;
            int _channel = DEFAULT_TOKEN_CHANNEL;
            // org/javaruntype/type/parser/Type.g:34:7: ( ',' )
            // org/javaruntype/type/parser/Type.g:34:9: ','
            {
            match(','); 

            }

            state.type = _type;
            state.channel = _channel;
        }
        finally {
        }
    }
    // $ANTLR end "COMMA"

    // $ANTLR start "EXTENDS"
    public final void mEXTENDS() throws RecognitionException {
        try {
            int _type = EXTENDS;
            int _channel = DEFAULT_TOKEN_CHANNEL;
            // org/javaruntype/type/parser/Type.g:35:9: ( ' extends ' )
            // org/javaruntype/type/parser/Type.g:35:11: ' extends '
            {
            match(" extends "); 


            }

            state.type = _type;
            state.channel = _channel;
        }
        finally {
        }
    }
    // $ANTLR end "EXTENDS"

    // $ANTLR start "SUPER"
    public final void mSUPER() throws RecognitionException {
        try {
            int _type = SUPER;
            int _channel = DEFAULT_TOKEN_CHANNEL;
            // org/javaruntype/type/parser/Type.g:36:7: ( ' super ' )
            // org/javaruntype/type/parser/Type.g:36:9: ' super '
            {
            match(" super "); 


            }

            state.type = _type;
            state.channel = _channel;
        }
        finally {
        }
    }
    // $ANTLR end "SUPER"

    // $ANTLR start "EXT"
    public final void mEXT() throws RecognitionException {
        try {
            int _type = EXT;
            int _channel = DEFAULT_TOKEN_CHANNEL;
            // org/javaruntype/type/parser/Type.g:37:5: ( 'EXT' )
            // org/javaruntype/type/parser/Type.g:37:7: 'EXT'
            {
            match("EXT"); 


            }

            state.type = _type;
            state.channel = _channel;
        }
        finally {
        }
    }
    // $ANTLR end "EXT"

    // $ANTLR start "SUP"
    public final void mSUP() throws RecognitionException {
        try {
            int _type = SUP;
            int _channel = DEFAULT_TOKEN_CHANNEL;
            // org/javaruntype/type/parser/Type.g:38:5: ( 'SUP' )
            // org/javaruntype/type/parser/Type.g:38:7: 'SUP'
            {
            match("SUP"); 


            }

            state.type = _type;
            state.channel = _channel;
        }
        finally {
        }
    }
    // $ANTLR end "SUP"

    // $ANTLR start "CLASSNAME"
    public final void mCLASSNAME() throws RecognitionException {
        try {
            int _type = CLASSNAME;
            int _channel = DEFAULT_TOKEN_CHANNEL;
            // org/javaruntype/type/parser/Type.g:120:5: ( ( 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '-' | '_' | '.' | '$' )+ )
            // org/javaruntype/type/parser/Type.g:120:7: ( 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '-' | '_' | '.' | '$' )+
            {
            // org/javaruntype/type/parser/Type.g:120:7: ( 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '-' | '_' | '.' | '$' )+
            int cnt1=0;
            loop1:
            do {
                int alt1=2;
                int LA1_0 = input.LA(1);

                if ( (LA1_0=='$'||(LA1_0>='-' && LA1_0<='.')||(LA1_0>='0' && LA1_0<='9')||(LA1_0>='A' && LA1_0<='Z')||LA1_0=='_'||(LA1_0>='a' && LA1_0<='z')) ) {
                    alt1=1;
                }


                switch (alt1) {
            	case 1 :
            	    // org/javaruntype/type/parser/Type.g:
            	    {
            	    if ( input.LA(1)=='$'||(input.LA(1)>='-' && input.LA(1)<='.')||(input.LA(1)>='0' && input.LA(1)<='9')||(input.LA(1)>='A' && input.LA(1)<='Z')||input.LA(1)=='_'||(input.LA(1)>='a' && input.LA(1)<='z') ) {
            	        input.consume();

            	    }
            	    else {
            	        MismatchedSetException mse = new MismatchedSetException(null,input);
            	        recover(mse);
            	        throw mse;}


            	    }
            	    break;

            	default :
            	    if ( cnt1 >= 1 ) break loop1;
                        EarlyExitException eee =
                            new EarlyExitException(1, input);
                        throw eee;
                }
                cnt1++;
            } while (true);


            }

            state.type = _type;
            state.channel = _channel;
        }
        finally {
        }
    }
    // $ANTLR end "CLASSNAME"

    // $ANTLR start "WS"
    public final void mWS() throws RecognitionException {
        try {
            int _type = WS;
            int _channel = DEFAULT_TOKEN_CHANNEL;
            // org/javaruntype/type/parser/Type.g:124:5: ( ( ' ' )+ )
            // org/javaruntype/type/parser/Type.g:124:7: ( ' ' )+
            {
            // org/javaruntype/type/parser/Type.g:124:7: ( ' ' )+
            int cnt2=0;
            loop2:
            do {
                int alt2=2;
                int LA2_0 = input.LA(1);

                if ( (LA2_0==' ') ) {
                    alt2=1;
                }


                switch (alt2) {
            	case 1 :
            	    // org/javaruntype/type/parser/Type.g:124:8: ' '
            	    {
            	    match(' '); 

            	    }
            	    break;

            	default :
            	    if ( cnt2 >= 1 ) break loop2;
                        EarlyExitException eee =
                            new EarlyExitException(2, input);
                        throw eee;
                }
                cnt2++;
            } while (true);

            skip();

            }

            state.type = _type;
            state.channel = _channel;
        }
        finally {
        }
    }
    // $ANTLR end "WS"

    public void mTokens() throws RecognitionException {
        // org/javaruntype/type/parser/Type.g:1:8: ( ARRAY | UNKNOWN | BEGINTYPEPARAM | ENDTYPEPARAM | COMMA | EXTENDS | SUPER | EXT | SUP | CLASSNAME | WS )
        int alt3=11;
        alt3 = dfa3.predict(input);
        switch (alt3) {
            case 1 :
                // org/javaruntype/type/parser/Type.g:1:10: ARRAY
                {
                mARRAY(); 

                }
                break;
            case 2 :
                // org/javaruntype/type/parser/Type.g:1:16: UNKNOWN
                {
                mUNKNOWN(); 

                }
                break;
            case 3 :
                // org/javaruntype/type/parser/Type.g:1:24: BEGINTYPEPARAM
                {
                mBEGINTYPEPARAM(); 

                }
                break;
            case 4 :
                // org/javaruntype/type/parser/Type.g:1:39: ENDTYPEPARAM
                {
                mENDTYPEPARAM(); 

                }
                break;
            case 5 :
                // org/javaruntype/type/parser/Type.g:1:52: COMMA
                {
                mCOMMA(); 

                }
                break;
            case 6 :
                // org/javaruntype/type/parser/Type.g:1:58: EXTENDS
                {
                mEXTENDS(); 

                }
                break;
            case 7 :
                // org/javaruntype/type/parser/Type.g:1:66: SUPER
                {
                mSUPER(); 

                }
                break;
            case 8 :
                // org/javaruntype/type/parser/Type.g:1:72: EXT
                {
                mEXT(); 

                }
                break;
            case 9 :
                // org/javaruntype/type/parser/Type.g:1:76: SUP
                {
                mSUP(); 

                }
                break;
            case 10 :
                // org/javaruntype/type/parser/Type.g:1:80: CLASSNAME
                {
                mCLASSNAME(); 

                }
                break;
            case 11 :
                // org/javaruntype/type/parser/Type.g:1:90: WS
                {
                mWS(); 

                }
                break;

        }

    }


    protected DFA3 dfa3 = new DFA3(this);
    static final String DFA3_eotS =
        "\6\uffff\1\14\2\11\4\uffff\2\11\1\21\1\22\2\uffff";
    static final String DFA3_eofS =
        "\23\uffff";
    static final String DFA3_minS =
        "\1\40\5\uffff\1\145\1\130\1\125\4\uffff\1\124\1\120\2\44\2\uffff";
    static final String DFA3_maxS =
        "\1\172\5\uffff\1\163\1\130\1\125\4\uffff\1\124\1\120\2\172\2\uffff";
    static final String DFA3_acceptS =
        "\1\uffff\1\1\1\2\1\3\1\4\1\5\3\uffff\1\12\1\6\1\7\1\13\4\uffff"+
        "\1\10\1\11";
    static final String DFA3_specialS =
        "\23\uffff}>";
    static final String[] DFA3_transitionS = {
            "\1\6\3\uffff\1\11\7\uffff\1\5\2\11\1\uffff\12\11\2\uffff\1"+
            "\3\1\uffff\1\4\1\2\1\uffff\4\11\1\7\15\11\1\10\7\11\1\1\3\uffff"+
            "\1\11\1\uffff\32\11",
            "",
            "",
            "",
            "",
            "",
            "\1\12\15\uffff\1\13",
            "\1\15",
            "\1\16",
            "",
            "",
            "",
            "",
            "\1\17",
            "\1\20",
            "\1\11\10\uffff\2\11\1\uffff\12\11\7\uffff\32\11\4\uffff\1"+
            "\11\1\uffff\32\11",
            "\1\11\10\uffff\2\11\1\uffff\12\11\7\uffff\32\11\4\uffff\1"+
            "\11\1\uffff\32\11",
            "",
            ""
    };

    static final short[] DFA3_eot = DFA.unpackEncodedString(DFA3_eotS);
    static final short[] DFA3_eof = DFA.unpackEncodedString(DFA3_eofS);
    static final char[] DFA3_min = DFA.unpackEncodedStringToUnsignedChars(DFA3_minS);
    static final char[] DFA3_max = DFA.unpackEncodedStringToUnsignedChars(DFA3_maxS);
    static final short[] DFA3_accept = DFA.unpackEncodedString(DFA3_acceptS);
    static final short[] DFA3_special = DFA.unpackEncodedString(DFA3_specialS);
    static final short[][] DFA3_transition;

    static {
        int numStates = DFA3_transitionS.length;
        DFA3_transition = new short[numStates][];
        for (int i=0; i<numStates; i++) {
            DFA3_transition[i] = DFA.unpackEncodedString(DFA3_transitionS[i]);
        }
    }

    class DFA3 extends DFA {

        public DFA3(BaseRecognizer recognizer) {
            this.recognizer = recognizer;
            this.decisionNumber = 3;
            this.eot = DFA3_eot;
            this.eof = DFA3_eof;
            this.min = DFA3_min;
            this.max = DFA3_max;
            this.accept = DFA3_accept;
            this.special = DFA3_special;
            this.transition = DFA3_transition;
        }
        public String getDescription() {
            return "1:1: Tokens : ( ARRAY | UNKNOWN | BEGINTYPEPARAM | ENDTYPEPARAM | COMMA | EXTENDS | SUPER | EXT | SUP | CLASSNAME | WS );";
        }
    }
 

} public class TypeParser extends Parser {
    public static final String[] tokenNames = new String[] {
        "<invalid>", "<EOR>", "<DOWN>", "<UP>", "ARRAY", "UNKNOWN", "BEGINTYPEPARAM", "ENDTYPEPARAM", "COMMA", "EXTENDS", "SUPER", "EXT", "SUP", "CLASSNAME", "WS"
    };
    public static final int WS=14;
    public static final int UNKNOWN=5;
    public static final int COMMA=8;
    public static final int EXT=11;
    public static final int ENDTYPEPARAM=7;
    public static final int SUPER=10;
    public static final int EOF=-1;
    public static final int EXTENDS=9;
    public static final int ARRAY=4;
    public static final int SUP=12;
    public static final int BEGINTYPEPARAM=6;
    public static final int CLASSNAME=13;

    // delegates
    // delegators


        public TypeParser(TokenStream input) {
            this(input, new RecognizerSharedState());
        }
        public TypeParser(TokenStream input, RecognizerSharedState state) {
            super(input, state);
             
        }
        
    protected TreeAdaptor adaptor = new CommonTreeAdaptor();

    public void setTreeAdaptor(TreeAdaptor adaptor) {
        this.adaptor = adaptor;
    }
    public TreeAdaptor getTreeAdaptor() {
        return adaptor;
    }

    public String[] getTokenNames() { return TypeParser.tokenNames; }
    public String getGrammarFileName() { return "org/javaruntype/type/parser/Type.g"; }


    public static class type_return extends ParserRuleReturnScope {
        CommonTree tree;
        public Object getTree() { return tree; }
    };

    // $ANTLR start "type"
    // org/javaruntype/type/parser/Type.g:100:1: type : typeExpression ;
    public final TypeParser.type_return type() throws RecognitionException {
        TypeParser.type_return retval = new TypeParser.type_return();
        retval.start = input.LT(1);

        CommonTree root_0 = null;

        TypeParser.typeExpression_return typeExpression1 = null;



        try {
            // org/javaruntype/type/parser/Type.g:101:5: ( typeExpression )
            // org/javaruntype/type/parser/Type.g:101:7: typeExpression
            {
            root_0 = (CommonTree)adaptor.nil();

            pushFollow(FOLLOW_typeExpression_in_type160);
            typeExpression1=typeExpression();

            state._fsp--;

            adaptor.addChild(root_0, typeExpression1.getTree());

            }

            retval.stop = input.LT(-1);

            retval.tree = (CommonTree)adaptor.rulePostProcessing(root_0);
            adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);

        }

            catch (RecognitionException e) {
            	throw e;
            }
        finally {
        }
        return retval;
    }
    // $ANTLR end "type"

    public static class typeExpression_return extends ParserRuleReturnScope {
        CommonTree tree;
        public Object getTree() { return tree; }
    };

    // $ANTLR start "typeExpression"
    // org/javaruntype/type/parser/Type.g:105:1: typeExpression : ( CLASSNAME (arr+= ARRAY )* -> ^( CLASSNAME ( $arr)* ) | CLASSNAME BEGINTYPEPARAM typePar+= typeParameterization ( COMMA typePar+= typeParameterization )* ENDTYPEPARAM (arr+= ARRAY )* -> ^( CLASSNAME ( $arr)* ( $typePar)+ ) );
    public final TypeParser.typeExpression_return typeExpression() throws RecognitionException {
        TypeParser.typeExpression_return retval = new TypeParser.typeExpression_return();
        retval.start = input.LT(1);

        CommonTree root_0 = null;

        Token CLASSNAME2=null;
        Token CLASSNAME3=null;
        Token BEGINTYPEPARAM4=null;
        Token COMMA5=null;
        Token ENDTYPEPARAM6=null;
        Token arr=null;
        List list_arr=null;
        List list_typePar=null;
        TypeParser.typeParameterization_return typePar = null;
         typePar = null;
        CommonTree CLASSNAME2_tree=null;
        CommonTree CLASSNAME3_tree=null;
        CommonTree BEGINTYPEPARAM4_tree=null;
        CommonTree COMMA5_tree=null;
        CommonTree ENDTYPEPARAM6_tree=null;
        CommonTree arr_tree=null;
        RewriteRuleTokenStream stream_ENDTYPEPARAM=new RewriteRuleTokenStream(adaptor,"token ENDTYPEPARAM");
        RewriteRuleTokenStream stream_COMMA=new RewriteRuleTokenStream(adaptor,"token COMMA");
        RewriteRuleTokenStream stream_ARRAY=new RewriteRuleTokenStream(adaptor,"token ARRAY");
        RewriteRuleTokenStream stream_BEGINTYPEPARAM=new RewriteRuleTokenStream(adaptor,"token BEGINTYPEPARAM");
        RewriteRuleTokenStream stream_CLASSNAME=new RewriteRuleTokenStream(adaptor,"token CLASSNAME");
        RewriteRuleSubtreeStream stream_typeParameterization=new RewriteRuleSubtreeStream(adaptor,"rule typeParameterization");
        try {
            // org/javaruntype/type/parser/Type.g:106:5: ( CLASSNAME (arr+= ARRAY )* -> ^( CLASSNAME ( $arr)* ) | CLASSNAME BEGINTYPEPARAM typePar+= typeParameterization ( COMMA typePar+= typeParameterization )* ENDTYPEPARAM (arr+= ARRAY )* -> ^( CLASSNAME ( $arr)* ( $typePar)+ ) )
            int alt4=2;
            int LA4_0 = input.LA(1);

            if ( (LA4_0==CLASSNAME) ) {
                int LA4_1 = input.LA(2);

                if ( (LA4_1==BEGINTYPEPARAM) ) {
                    alt4=2;
                }
                else if ( (LA4_1==EOF||LA4_1==ARRAY||(LA4_1>=ENDTYPEPARAM && LA4_1<=COMMA)) ) {
                    alt4=1;
                }
                else {
                    NoViableAltException nvae =
                        new NoViableAltException("", 4, 1, input);

                    throw nvae;
                }
            }
            else {
                NoViableAltException nvae =
                    new NoViableAltException("", 4, 0, input);

                throw nvae;
            }
            switch (alt4) {
                case 1 :
                    // org/javaruntype/type/parser/Type.g:106:7: CLASSNAME (arr+= ARRAY )*
                    {
                    CLASSNAME2=(Token)match(input,CLASSNAME,FOLLOW_CLASSNAME_in_typeExpression178);  
                    stream_CLASSNAME.add(CLASSNAME2);

                    // org/javaruntype/type/parser/Type.g:106:17: (arr+= ARRAY )*
                    loop1:
                    do {
                        int alt1=2;
                        int LA1_0 = input.LA(1);

                        if ( (LA1_0==ARRAY) ) {
                            alt1=1;
                        }


                        switch (alt1) {
                    	case 1 :
                    	    // org/javaruntype/type/parser/Type.g:106:18: arr+= ARRAY
                    	    {
                    	    arr=(Token)match(input,ARRAY,FOLLOW_ARRAY_in_typeExpression183);  
                    	    stream_ARRAY.add(arr);

                    	    if (list_arr==null) list_arr=new ArrayList();
                    	    list_arr.add(arr);


                    	    }
                    	    break;

                    	default :
                    	    break loop1;
                        }
                    } while (true);



                    // AST REWRITE
                    // elements: CLASSNAME, arr
                    // token labels: 
                    // rule labels: retval
                    // token list labels: arr
                    // rule list labels: 
                    // wildcard labels: 
                    retval.tree = root_0;
                    RewriteRuleTokenStream stream_arr=new RewriteRuleTokenStream(adaptor,"token arr", list_arr);
                    RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"rule retval",retval!=null?retval.tree:null);

                    root_0 = (CommonTree)adaptor.nil();
                    // 106:36: -> ^( CLASSNAME ( $arr)* )
                    {
                        // org/javaruntype/type/parser/Type.g:106:39: ^( CLASSNAME ( $arr)* )
                        {
                        CommonTree root_1 = (CommonTree)adaptor.nil();
                        root_1 = (CommonTree)adaptor.becomeRoot(stream_CLASSNAME.nextNode(), root_1);

                        // org/javaruntype/type/parser/Type.g:106:51: ( $arr)*
                        while ( stream_arr.hasNext() ) {
                            adaptor.addChild(root_1, stream_arr.nextNode());

                        }
                        stream_arr.reset();

                        adaptor.addChild(root_0, root_1);
                        }

                    }

                    retval.tree = root_0;
                    }
                    break;
                case 2 :
                    // org/javaruntype/type/parser/Type.g:107:7: CLASSNAME BEGINTYPEPARAM typePar+= typeParameterization ( COMMA typePar+= typeParameterization )* ENDTYPEPARAM (arr+= ARRAY )*
                    {
                    CLASSNAME3=(Token)match(input,CLASSNAME,FOLLOW_CLASSNAME_in_typeExpression208);  
                    stream_CLASSNAME.add(CLASSNAME3);

                    BEGINTYPEPARAM4=(Token)match(input,BEGINTYPEPARAM,FOLLOW_BEGINTYPEPARAM_in_typeExpression210);  
                    stream_BEGINTYPEPARAM.add(BEGINTYPEPARAM4);

                    pushFollow(FOLLOW_typeParameterization_in_typeExpression214);
                    typePar=typeParameterization();

                    state._fsp--;

                    stream_typeParameterization.add(typePar.getTree());
                    if (list_typePar==null) list_typePar=new ArrayList();
                    list_typePar.add(typePar.getTree());

                    // org/javaruntype/type/parser/Type.g:107:62: ( COMMA typePar+= typeParameterization )*
                    loop2:
                    do {
                        int alt2=2;
                        int LA2_0 = input.LA(1);

                        if ( (LA2_0==COMMA) ) {
                            alt2=1;
                        }


                        switch (alt2) {
                    	case 1 :
                    	    // org/javaruntype/type/parser/Type.g:107:63: COMMA typePar+= typeParameterization
                    	    {
                    	    COMMA5=(Token)match(input,COMMA,FOLLOW_COMMA_in_typeExpression217);  
                    	    stream_COMMA.add(COMMA5);

                    	    pushFollow(FOLLOW_typeParameterization_in_typeExpression221);
                    	    typePar=typeParameterization();

                    	    state._fsp--;

                    	    stream_typeParameterization.add(typePar.getTree());
                    	    if (list_typePar==null) list_typePar=new ArrayList();
                    	    list_typePar.add(typePar.getTree());


                    	    }
                    	    break;

                    	default :
                    	    break loop2;
                        }
                    } while (true);

                    ENDTYPEPARAM6=(Token)match(input,ENDTYPEPARAM,FOLLOW_ENDTYPEPARAM_in_typeExpression225);  
                    stream_ENDTYPEPARAM.add(ENDTYPEPARAM6);

                    // org/javaruntype/type/parser/Type.g:107:114: (arr+= ARRAY )*
                    loop3:
                    do {
                        int alt3=2;
                        int LA3_0 = input.LA(1);

                        if ( (LA3_0==ARRAY) ) {
                            alt3=1;
                        }


                        switch (alt3) {
                    	case 1 :
                    	    // org/javaruntype/type/parser/Type.g:107:115: arr+= ARRAY
                    	    {
                    	    arr=(Token)match(input,ARRAY,FOLLOW_ARRAY_in_typeExpression230);  
                    	    stream_ARRAY.add(arr);

                    	    if (list_arr==null) list_arr=new ArrayList();
                    	    list_arr.add(arr);


                    	    }
                    	    break;

                    	default :
                    	    break loop3;
                        }
                    } while (true);



                    // AST REWRITE
                    // elements: arr, typePar, CLASSNAME
                    // token labels: 
                    // rule labels: retval
                    // token list labels: arr
                    // rule list labels: typePar
                    // wildcard labels: 
                    retval.tree = root_0;
                    RewriteRuleTokenStream stream_arr=new RewriteRuleTokenStream(adaptor,"token arr", list_arr);
                    RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"rule retval",retval!=null?retval.tree:null);
                    RewriteRuleSubtreeStream stream_typePar=new RewriteRuleSubtreeStream(adaptor,"token typePar",list_typePar);
                    root_0 = (CommonTree)adaptor.nil();
                    // 107:133: -> ^( CLASSNAME ( $arr)* ( $typePar)+ )
                    {
                        // org/javaruntype/type/parser/Type.g:107:136: ^( CLASSNAME ( $arr)* ( $typePar)+ )
                        {
                        CommonTree root_1 = (CommonTree)adaptor.nil();
                        root_1 = (CommonTree)adaptor.becomeRoot(stream_CLASSNAME.nextNode(), root_1);

                        // org/javaruntype/type/parser/Type.g:107:148: ( $arr)*
                        while ( stream_arr.hasNext() ) {
                            adaptor.addChild(root_1, stream_arr.nextNode());

                        }
                        stream_arr.reset();
                        if ( !(stream_typePar.hasNext()) ) {
                            throw new RewriteEarlyExitException();
                        }
                        while ( stream_typePar.hasNext() ) {
                            adaptor.addChild(root_1, stream_typePar.nextTree());

                        }
                        stream_typePar.reset();

                        adaptor.addChild(root_0, root_1);
                        }

                    }

                    retval.tree = root_0;
                    }
                    break;

            }
            retval.stop = input.LT(-1);

            retval.tree = (CommonTree)adaptor.rulePostProcessing(root_0);
            adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);

        }

            catch (RecognitionException e) {
            	throw e;
            }
        finally {
        }
        return retval;
    }
    // $ANTLR end "typeExpression"

    public static class typeParameterization_return extends ParserRuleReturnScope {
        CommonTree tree;
        public Object getTree() { return tree; }
    };

    // $ANTLR start "typeParameterization"
    // org/javaruntype/type/parser/Type.g:111:1: typeParameterization : ( typeExpression | UNKNOWN | UNKNOWN EXTENDS typExp= typeExpression -> ^( EXT $typExp) | UNKNOWN SUPER typExp= typeExpression -> ^( SUP $typExp) );
    public final TypeParser.typeParameterization_return typeParameterization() throws RecognitionException {
        TypeParser.typeParameterization_return retval = new TypeParser.typeParameterization_return();
        retval.start = input.LT(1);

        CommonTree root_0 = null;

        Token UNKNOWN8=null;
        Token UNKNOWN9=null;
        Token EXTENDS10=null;
        Token UNKNOWN11=null;
        Token SUPER12=null;
        TypeParser.typeExpression_return typExp = null;

        TypeParser.typeExpression_return typeExpression7 = null;


        CommonTree UNKNOWN8_tree=null;
        CommonTree UNKNOWN9_tree=null;
        CommonTree EXTENDS10_tree=null;
        CommonTree UNKNOWN11_tree=null;
        CommonTree SUPER12_tree=null;
        RewriteRuleTokenStream stream_SUPER=new RewriteRuleTokenStream(adaptor,"token SUPER");
        RewriteRuleTokenStream stream_UNKNOWN=new RewriteRuleTokenStream(adaptor,"token UNKNOWN");
        RewriteRuleTokenStream stream_EXTENDS=new RewriteRuleTokenStream(adaptor,"token EXTENDS");
        RewriteRuleSubtreeStream stream_typeExpression=new RewriteRuleSubtreeStream(adaptor,"rule typeExpression");
        try {
            // org/javaruntype/type/parser/Type.g:112:5: ( typeExpression | UNKNOWN | UNKNOWN EXTENDS typExp= typeExpression -> ^( EXT $typExp) | UNKNOWN SUPER typExp= typeExpression -> ^( SUP $typExp) )
            int alt5=4;
            int LA5_0 = input.LA(1);

            if ( (LA5_0==CLASSNAME) ) {
                alt5=1;
            }
            else if ( (LA5_0==UNKNOWN) ) {
                switch ( input.LA(2) ) {
                case EXTENDS:
                    {
                    alt5=3;
                    }
                    break;
                case SUPER:
                    {
                    alt5=4;
                    }
                    break;
                case ENDTYPEPARAM:
                case COMMA:
                    {
                    alt5=2;
                    }
                    break;
                default:
                    NoViableAltException nvae =
                        new NoViableAltException("", 5, 2, input);

                    throw nvae;
                }

            }
            else {
                NoViableAltException nvae =
                    new NoViableAltException("", 5, 0, input);

                throw nvae;
            }
            switch (alt5) {
                case 1 :
                    // org/javaruntype/type/parser/Type.g:112:7: typeExpression
                    {
                    root_0 = (CommonTree)adaptor.nil();

                    pushFollow(FOLLOW_typeExpression_in_typeParameterization291);
                    typeExpression7=typeExpression();

                    state._fsp--;

                    adaptor.addChild(root_0, typeExpression7.getTree());

                    }
                    break;
                case 2 :
                    // org/javaruntype/type/parser/Type.g:113:7: UNKNOWN
                    {
                    root_0 = (CommonTree)adaptor.nil();

                    UNKNOWN8=(Token)match(input,UNKNOWN,FOLLOW_UNKNOWN_in_typeParameterization299); 
                    UNKNOWN8_tree = (CommonTree)adaptor.create(UNKNOWN8);
                    adaptor.addChild(root_0, UNKNOWN8_tree);


                    }
                    break;
                case 3 :
                    // org/javaruntype/type/parser/Type.g:114:7: UNKNOWN EXTENDS typExp= typeExpression
                    {
                    UNKNOWN9=(Token)match(input,UNKNOWN,FOLLOW_UNKNOWN_in_typeParameterization307);  
                    stream_UNKNOWN.add(UNKNOWN9);

                    EXTENDS10=(Token)match(input,EXTENDS,FOLLOW_EXTENDS_in_typeParameterization309);  
                    stream_EXTENDS.add(EXTENDS10);

                    pushFollow(FOLLOW_typeExpression_in_typeParameterization313);
                    typExp=typeExpression();

                    state._fsp--;

                    stream_typeExpression.add(typExp.getTree());


                    // AST REWRITE
                    // elements: typExp
                    // token labels: 
                    // rule labels: retval, typExp
                    // token list labels: 
                    // rule list labels: 
                    // wildcard labels: 
                    retval.tree = root_0;
                    RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"rule retval",retval!=null?retval.tree:null);
                    RewriteRuleSubtreeStream stream_typExp=new RewriteRuleSubtreeStream(adaptor,"rule typExp",typExp!=null?typExp.tree:null);

                    root_0 = (CommonTree)adaptor.nil();
                    // 114:45: -> ^( EXT $typExp)
                    {
                        // org/javaruntype/type/parser/Type.g:114:48: ^( EXT $typExp)
                        {
                        CommonTree root_1 = (CommonTree)adaptor.nil();
                        root_1 = (CommonTree)adaptor.becomeRoot((CommonTree)adaptor.create(EXT, "EXT"), root_1);

                        adaptor.addChild(root_1, stream_typExp.nextTree());

                        adaptor.addChild(root_0, root_1);
                        }

                    }

                    retval.tree = root_0;
                    }
                    break;
                case 4 :
                    // org/javaruntype/type/parser/Type.g:115:7: UNKNOWN SUPER typExp= typeExpression
                    {
                    UNKNOWN11=(Token)match(input,UNKNOWN,FOLLOW_UNKNOWN_in_typeParameterization330);  
                    stream_UNKNOWN.add(UNKNOWN11);

                    SUPER12=(Token)match(input,SUPER,FOLLOW_SUPER_in_typeParameterization332);  
                    stream_SUPER.add(SUPER12);

                    pushFollow(FOLLOW_typeExpression_in_typeParameterization336);
                    typExp=typeExpression();

                    state._fsp--;

                    stream_typeExpression.add(typExp.getTree());


                    // AST REWRITE
                    // elements: typExp
                    // token labels: 
                    // rule labels: retval, typExp
                    // token list labels: 
                    // rule list labels: 
                    // wildcard labels: 
                    retval.tree = root_0;
                    RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"rule retval",retval!=null?retval.tree:null);
                    RewriteRuleSubtreeStream stream_typExp=new RewriteRuleSubtreeStream(adaptor,"rule typExp",typExp!=null?typExp.tree:null);

                    root_0 = (CommonTree)adaptor.nil();
                    // 115:43: -> ^( SUP $typExp)
                    {
                        // org/javaruntype/type/parser/Type.g:115:46: ^( SUP $typExp)
                        {
                        CommonTree root_1 = (CommonTree)adaptor.nil();
                        root_1 = (CommonTree)adaptor.becomeRoot((CommonTree)adaptor.create(SUP, "SUP"), root_1);

                        adaptor.addChild(root_1, stream_typExp.nextTree());

                        adaptor.addChild(root_0, root_1);
                        }

                    }

                    retval.tree = root_0;
                    }
                    break;

            }
            retval.stop = input.LT(-1);

            retval.tree = (CommonTree)adaptor.rulePostProcessing(root_0);
            adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);

        }

            catch (RecognitionException e) {
            	throw e;
            }
        finally {
        }
        return retval;
    }
    // $ANTLR end "typeParameterization"

    // Delegated rules


 

    public static final BitSet FOLLOW_typeExpression_in_type160 = new BitSet(new long[]{0x0000000000000002L});
    public static final BitSet FOLLOW_CLASSNAME_in_typeExpression178 = new BitSet(new long[]{0x0000000000000012L});
    public static final BitSet FOLLOW_ARRAY_in_typeExpression183 = new BitSet(new long[]{0x0000000000000012L});
    public static final BitSet FOLLOW_CLASSNAME_in_typeExpression208 = new BitSet(new long[]{0x0000000000000040L});
    public static final BitSet FOLLOW_BEGINTYPEPARAM_in_typeExpression210 = new BitSet(new long[]{0x0000000000002020L});
    public static final BitSet FOLLOW_typeParameterization_in_typeExpression214 = new BitSet(new long[]{0x0000000000000180L});
    public static final BitSet FOLLOW_COMMA_in_typeExpression217 = new BitSet(new long[]{0x0000000000002020L});
    public static final BitSet FOLLOW_typeParameterization_in_typeExpression221 = new BitSet(new long[]{0x0000000000000180L});
    public static final BitSet FOLLOW_ENDTYPEPARAM_in_typeExpression225 = new BitSet(new long[]{0x0000000000000012L});
    public static final BitSet FOLLOW_ARRAY_in_typeExpression230 = new BitSet(new long[]{0x0000000000000012L});
    public static final BitSet FOLLOW_typeExpression_in_typeParameterization291 = new BitSet(new long[]{0x0000000000000002L});
    public static final BitSet FOLLOW_UNKNOWN_in_typeParameterization299 = new BitSet(new long[]{0x0000000000000002L});
    public static final BitSet FOLLOW_UNKNOWN_in_typeParameterization307 = new BitSet(new long[]{0x0000000000000200L});
    public static final BitSet FOLLOW_EXTENDS_in_typeParameterization309 = new BitSet(new long[]{0x0000000000002000L});
    public static final BitSet FOLLOW_typeExpression_in_typeParameterization313 = new BitSet(new long[]{0x0000000000000002L});
    public static final BitSet FOLLOW_UNKNOWN_in_typeParameterization330 = new BitSet(new long[]{0x0000000000000400L});
    public static final BitSet FOLLOW_SUPER_in_typeParameterization332 = new BitSet(new long[]{0x0000000000002000L});
    public static final BitSet FOLLOW_typeExpression_in_typeParameterization336 = new BitSet(new long[]{0x0000000000000002L});

} public final class BoundedTypeDefVariable implements TypeDefVariable {

    
    private static final long serialVersionUID = -7857569027190529682L;
    
    private final String variableName;
    private final InnerTypeDefVariable[] bounds;
    
    private final String stringRepresentation;
    
    private final int hashCode;


    
    private static String createStringRepresentation(final String variableName, 
            final InnerTypeDefVariable[] bounds) {
        
        final StringBuilder str = new StringBuilder();
        str.append(variableName);
        str.append(" extends ");
        str.append(Utils.join(bounds, "&"));
        return str.toString();
        
    }
    
    
    BoundedTypeDefVariable(final String variableName, 
            final InnerTypeDefVariable[] bounds) {
        this.variableName = variableName;
        this.bounds = bounds;
        this.stringRepresentation = 
            createStringRepresentation(variableName, bounds);
        this.hashCode = this.stringRepresentation.hashCode();
    }
    
    
    public String getVariableName() {
        return this.variableName;
    }


    /**
     * <p>
     * Returns the bounds specified for the type definition variable.
     * </p>
     * 
     * @return the bounds.
     */
    public InnerTypeDefVariable[] getBounds() {
        return this.bounds.clone();
    }


    @Override
    public int hashCode() {
        return this.hashCode;
    }


    @Override
    public boolean equals(final Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final BoundedTypeDefVariable other = (BoundedTypeDefVariable) obj;
        return this.stringRepresentation.equals(other.stringRepresentation);
    }


    @Override
    public String toString() {
        return this.stringRepresentation;
    }
    
    
} public final class InnerClassTypeDefVariable implements InnerTypeDefVariable {

    private static final long serialVersionUID = -8577766318173021770L;
    
    private final Class<?> componentClass;
    private final int arrayDimensions;
    
    private final String stringRepresentation;
    
    private final int hashCode;
    

    private static String createStringRepresentation(
            final Class<?> componentClass, final int arrayDimensions) {
        final StringBuilder str = new StringBuilder();
        str.append(componentClass.getName());
        for (int i = 0; i < arrayDimensions; i++) {
            str.append("[]");
        }
        return str.toString();
    }
    
    InnerClassTypeDefVariable(final Class<?> componentClass, final int arrayDimensions) {
        super();
        this.componentClass = componentClass;
        this.arrayDimensions = arrayDimensions;
        this.stringRepresentation = 
            createStringRepresentation(componentClass, arrayDimensions);
        this.hashCode = this.stringRepresentation.hashCode();
    }


    /**
     * <p>
     * Returns the component class
     * </p>
     * 
     * @return the component class
     */
    public Class<?> getComponentClass() {
        return this.componentClass;
    }
    
    
    /**
     * <p>
     * Returns the number of array dimensions applied to the component class.
     * </p>
     * 
     * @return the array dimensions.
     */
    public int getArrayDimensions() {
        return this.arrayDimensions;
    }


    @Override
    public int hashCode() {
        return this.hashCode;
    }

    
    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final InnerClassTypeDefVariable other = (InnerClassTypeDefVariable) obj;
        return this.stringRepresentation.equals(other.stringRepresentation);
    }

    
    @Override
    public String toString() {
        return this.stringRepresentation;
    }

    
    
} public final class InnerNamedTypeDefVariable implements InnerTypeDefVariable {
    
    private static final long serialVersionUID = -1687200742438119530L;
    
    private final String variableName;
    private final int arrayDimensions;
    
    private final String stringRepresentation;
    
    private final int hashCode;
    

    private static String createStringRepresentation(
            final String variableName, final int arrayDimensions) {
        final StringBuilder str = new StringBuilder();
        str.append(variableName);
        for (int i = 0; i < arrayDimensions; i++) {
            str.append("[]");
        }
        return str.toString();
    }

    
    InnerNamedTypeDefVariable(final String variableName, final int arrayDimensions) {
        this.variableName = variableName;
        this.arrayDimensions = arrayDimensions;
        this.stringRepresentation = 
            createStringRepresentation(variableName, arrayDimensions);
        this.hashCode = this.stringRepresentation.hashCode();
    }


    /**
     * <p>
     * Returns the component class
     * </p>
     * 
     * @return the component class
     */
    public String getVariableName() {
        return this.variableName;
    }
    
    
    /**
     * <p>
     * Returns the number of array dimensions applied to the component class.
     * </p>
     * 
     * @return the array dimensions.
     */
    public int getArrayDimensions() {
        return this.arrayDimensions;
    }
    

    @Override
    public int hashCode() {
        return this.hashCode;
    }


    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final InnerNamedTypeDefVariable other = (InnerNamedTypeDefVariable) obj;
        return this.stringRepresentation.equals(other.stringRepresentation);
    }


    @Override
    public String toString() {
        return this.stringRepresentation;
    }

    
} public final class InnerParameterizedTypeTypeDefVariable 
        implements InnerTypeDefVariable {

    private static final long serialVersionUID = 7978451943834222992L;
    
    private final Class<?> componentClass;
    private final InnerTypeDefVariable[] variables;
    private final int arrayDimensions;
    
    private final String stringRepresentation;
    
    private final int hashCode;
    
    
    
    private static String createStringRepresentation(final Class<?> componentClass,
            final InnerTypeDefVariable[] variables, final int arrayDimensions) {
        
        final StringBuilder str = new StringBuilder();
        str.append(componentClass.getName());
        str.append("<");
        str.append(Utils.join(variables, ","));
        str.append(">");
        for (int i = 0; i < arrayDimensions; i++) {
            str.append("[]");
        }
        return str.toString();
        
    }
    
    
    
    InnerParameterizedTypeTypeDefVariable(final Class<?> componentClass,
            final InnerTypeDefVariable[] variables, final int arrayDimensions) {
        this.componentClass = componentClass;
        this.variables = variables;
        this.arrayDimensions = arrayDimensions;
        this.stringRepresentation = 
            createStringRepresentation(
                    componentClass, variables, arrayDimensions);
        this.hashCode = this.stringRepresentation.hashCode();
    }


    /**
     * <p>
     * Returns the component class
     * </p>
     * 
     * @return the component class
     */
    public Class<?> getComponentClass() {
        return this.componentClass;
    }


    /**
     * Returns the type parameters (variables) applied to the component class.
     * 
     * @return the type parameters
     */
    public InnerTypeDefVariable[] getVariables() {
        return this.variables.clone();
    }
    
    
    /**
     * <p>
     * Returns the number of array dimensions applied to the component class.
     * </p>
     * 
     * @return the array dimensions.
     */
    public int getArrayDimensions() {
        return this.arrayDimensions;
    }
    

    @Override
    public int hashCode() {
        return this.hashCode;
    }


    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final InnerParameterizedTypeTypeDefVariable other = (InnerParameterizedTypeTypeDefVariable) obj;
        return this.stringRepresentation.equals(other.stringRepresentation);
    }


    @Override
    public String toString() {
        return this.stringRepresentation;
    }

    
} public interface InnerTypeDefVariable extends Serializable {
    
    // Empty. Just a marker interface
    
} public final class InnerWildcardTypeDefVariable implements InnerTypeDefVariable {

    private static final long serialVersionUID = -8238722236323950838L;
    
    private final InnerTypeDefVariable upperBound;
    private final InnerTypeDefVariable lowerBound;
    
    private final String stringRepresentation;
    
    private final int hashCode;
    

    private static String createStringRepresentation(
            final InnerTypeDefVariable upperBound, final InnerTypeDefVariable lowerBound) {
        final StringBuilder str = new StringBuilder();
        str.append("?");
        if (upperBound != null) {
            str.append(" extends ");
            str.append(upperBound.toString());
        } else if (lowerBound != null) {
            str.append(" super ");
            str.append(lowerBound.toString());
        } else {
            return str.toString();
        }
        return str.toString();
    }

    
    InnerWildcardTypeDefVariable(final InnerTypeDefVariable upperBound, 
            final InnerTypeDefVariable lowerBound) {
        super();
        // Cannot have lower and upper bound at a time
        Utils.validateIsTrue(!(upperBound != null) && (lowerBound != null));
        this.upperBound = upperBound;
        this.lowerBound = lowerBound;
        this.stringRepresentation = 
            createStringRepresentation(upperBound, lowerBound);
        this.hashCode = this.stringRepresentation.hashCode();
    }


    /**
     * <p>
     * Returns whether the variable establishes an upper bound (with an <tt>extends</tt> clause).
     * </p>
     * 
     * @return whether the variable establishes an upper bound
     */
    public boolean hasUpperBound() {
        return (this.upperBound != null);
    }

    
    /**
     * <p>
     * Returns the variable upper bound (defined with an <tt>extends</tt> clause), if any.
     * </p>
     * 
     * @return the upper bound
     */
    public InnerTypeDefVariable getUpperBound() {
        return this.upperBound;
    }

    
    /**
     * <p>
     * Returns whether the variable establishes a lower bound (with a <tt>super</tt> clause).
     * </p>
     * 
     * @return whether the variable establishes a lower bound
     */
    public boolean hasLowerBound() {
        return (this.lowerBound != null);
    }

    
    /**
     * <p>
     * Returns the variable upper bound (defined with an <tt>extends</tt> clause), if any.
     * </p>
     * 
     * @return the upper bound
     */
    public InnerTypeDefVariable getLowerBound() {
        return this.lowerBound;
    }

    
    /**
     * <p>
     * Returns true only if there is neither an upper nor a lower bound, false otherwise.
     * </p>
     * 
     * @return whether there are bounds
     */
    public boolean isUnbound() {
        return ((this.upperBound == null) && (this.lowerBound == null));
    }


    @Override
    public int hashCode() {
        return this.hashCode;
    }


    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final InnerWildcardTypeDefVariable other = (InnerWildcardTypeDefVariable) obj;
        return this.stringRepresentation.equals(other.stringRepresentation);
    }


    @Override
    public String toString() {
        return this.stringRepresentation;
    }

    
    
} public final class NamedTypeDefVariable implements TypeDefVariable {
    
    private static final long serialVersionUID = 2342239160968747663L;
    
    private final String variableName;

    
    NamedTypeDefVariable(final String name) {
        this.variableName = name;
    }


    public String getVariableName() {
        return this.variableName;
    }


    @Override
    public int hashCode() {
        return this.variableName.hashCode();
    }


    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final NamedTypeDefVariable other = (NamedTypeDefVariable) obj;
        return this.variableName.equals(other.variableName);
    }


    @Override
    public String toString() {
        return this.variableName;
    }

    
} public final class TypeDef implements Serializable {
    
    private static final long serialVersionUID = 3446428616087989437L;

    private static final String NAME_TYPE_VARIABLES_START = "<";
    private static final String NAME_TYPE_VARIABLES_END = ">";
    private static final String NAME_TYPE_VARIABLES_SEPARATOR = ",";
    
    private final Class<?> componentClass;
    private final TypeDefVariable[] variables;
    private final String name;
    
    // Class is immutable, so hashCode can be precomputed
    private final int hashCode;


    private static String createName(final Class<?> componentClass, 
            final TypeDefVariable[] variables) {
        
        final StringBuilder str = new StringBuilder();
        str.append(componentClass.getName());
        if (variables.length > 0) {
            str.append(NAME_TYPE_VARIABLES_START);
            str.append(Utils.join(variables, NAME_TYPE_VARIABLES_SEPARATOR));
            str.append(NAME_TYPE_VARIABLES_END);
        }
        return str.toString();
        
    }
    
    
    TypeDef(final Class<?> componentClass, final TypeDefVariable[] variables) {
        
        Utils.validateNotNull(componentClass, 
                "Component class cannot be null");
        Utils.validateNotNull(variables, 
                "Type parameters cannot be null");

        this.componentClass = componentClass;
        this.variables = variables.clone(); 
        this.name = createName(componentClass, variables);
        this.hashCode = this.name.hashCode();
        
    }
    
    
    /**
     * 
     * Returns the component class of the type definition. For example, if the type definition
     * is <tt>java.util.Collection&lt;E&gt;</tt>, this method will return
     * <tt>java.lang.Class&lt;java.util.Collection&gt;</tt>
     * 
     * @return the component class.
     */
    public Class<?> getComponentClass() {
        return this.componentClass;
    }
    
    
    /**
     * Returns the variables of the type definition.
     * 
     * @return the type definition variables.
     */
    public TypeDefVariable[] getVariables() {
        return this.variables.clone();
    }
    
    
    /**
     * Returns the name of the type definition. For example: "java.util.Collection&lt;E&gt;"
     * 
     * @return the name of the type definition.
     */
    public String getName() {
        return this.name;
    }

    
    @Override
    public String toString() {
        return getName();
    }


    @Override
    public int hashCode() {
        return this.hashCode;
    }


    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final TypeDef other = (TypeDef) obj;
        // Comparing name will be enough and quicker than other comparisons
        return (this.name.equals(other.name));
    }
    
    
    @SuppressWarnings("unused")
    private Object readResolve() throws ObjectStreamException {
        final TypeDefRegistry typeDefRegistry = TypeDefRegistry.getInstance();
        return typeDefRegistry.forClass(this.componentClass);
    }

    
} public interface TypeDefVariable extends Serializable {
    
    /**
     * Returns the variable name.
     * 
     * @return the variable name.
     */
    public String getVariableName();
    
} public final class TypeDefs {


    /**
     * <p>
     * Retrieves the <tt>TypeDef</tt> object corresponding with the given class.
     * </p>
     * 
     * @param typeClass the class which TypeDef must be obtained.
     * @return the TypeDef
     */
    public static TypeDef forClass(final Class<?> typeClass) {
        
        Utils.validateNotNull(typeClass, "Class cannot be null");
        Utils.validateIsTrue(!typeClass.isArray(), "Cannot obtain TypeDef from array class");
        
        final TypeDefRegistry typeDefRegistry = TypeDefRegistry.getInstance();
        return typeDefRegistry.forClass(typeClass);
        
    }
    
    
    private TypeDefs() {
        super();
    }
    
} public class Utils {
    

    private static Map<String,String> primitiveClassAbbrevs = new HashMap<String,String>();

    
    static {
        primitiveClassAbbrevs.put("int", "I");
        primitiveClassAbbrevs.put("boolean", "Z");
        primitiveClassAbbrevs.put("float", "F");
        primitiveClassAbbrevs.put("long", "J");
        primitiveClassAbbrevs.put("short", "S");
        primitiveClassAbbrevs.put("byte", "B");
        primitiveClassAbbrevs.put("double", "D");
        primitiveClassAbbrevs.put("char", "C");
    }

    
    

    private Utils() {
      super();
    }

    
    /**
     * <p>
     * Internal utility method. DO NOT use this method directly.
     * </p>
     *
     * @param object target object
     * @param message message to be applied to exception if object is null
     * @throws IllegalArgumentException if target is null
     */
    public static void validateNotNull(final Object object, final String message) {
        if (object == null) {
            throw new IllegalArgumentException(message);
        }
    }
    

    /**
     * <p>
     * Internal utility method. DO NOT use this method directly.
     * </p>
     *
     * @param object target object
     * @throws IllegalArgumentException if target is null
     */
    public static void validateNotNull(final Object object) {
        if (object == null) {
            throw new IllegalArgumentException("The validated object is null");
        }
    }

    
    /**
     * <p>
     * Internal utility method. DO NOT use this method directly.
     * </p>
     *
     * @param expression expression to be validated
     * @throws IllegalArgumentException if expression is false
     */
    public static void validateIsTrue(final boolean expression) {
        if (expression == false) {
            throw new IllegalArgumentException("The validated expression is false");
        }
    }


    /**
     * <p>
     * Internal utility method. DO NOT use this method directly.
     * </p>
     *
     * @param expression expression to be validated
     * @param message message to be applied to the IllegalArgumentException if expression is false
     * @throws IllegalArgumentException if expression is false
     */
    public static void validateIsTrue(final boolean expression, final String message) {
        if (expression == false) {
            throw new IllegalArgumentException(message);
        }
    }


    /**
     * <p>
     * Internal utility method. DO NOT use this method directly.
     * </p>
     *
     * @param left left side of the comparison
     * @param right left side of the comparison
     * @return true if both arrays are equal
     */
    public static boolean isArrayEqual(final Object[] left, final Object[] right) {
        if (left == right) {
            return true;
        }
        if (left == null || right == null) {
            return false;
        }
        if (left.length != right.length) {
            return false;
        }
        for (int i = 0; i < left.length; ++i) {
            if (!isArrayElementEqual(left[i], right[i])) {
                return false;
            }
        }
        return true;
    }
    
    
    
    private static boolean isArrayElementEqual(final Object left, final Object right) {
        if (left == right) {
            return true;
        }
        if (left == null || right == null) {
            return false;
        }
        final Class<?> leftClass = left.getClass();
        if (!leftClass.isArray()) {
            if (left instanceof java.math.BigDecimal) {
                if (right instanceof BigDecimal) {
                    return (((BigDecimal)left).compareTo((BigDecimal)right) == 0);
                }
                return false;
            }
            return left.equals(right);
        } else if (left.getClass() != right.getClass()) {
            return false;
        }
        return isArrayEqual((Object[]) left, (Object[]) right);
    }

    
    /**
     * <p>
     * Internal utility method. DO NOT use this method directly.
     * </p>
     *
     * @param array array containing the elements to be joined
     * @param separator separator to be used for the joined result
     * @return the array elements, converted to String and joined with the separator
     */
    public static String join(Object[] array, String separator) {
        if (array == null) {
            return null;
        }
        StringBuilder strBuilder = new StringBuilder();
        for (int i = 0, z = array.length; i < z; i++) {
            if (i > 0) {
                strBuilder.append(separator);
            }
            strBuilder.append(array[i]);
        }
        return strBuilder.toString();
    }


    /**
     * <p>
     * Internal utility method. DO NOT use this method directly.
     * </p>
     *
     * @param string text from which all whitespace will be removed
     * @return the text without whitespace
     */
    public static String removeAllWhitespaces(final String string) {
        if (string == null || string.length() == 0) {
            return string;
        }
        final int originalSize = string.length();
        final char[] charArray = new char[originalSize];
        int charCount = 0;
        for (int i = 0; i < originalSize; i++) {
            final char currentChar = string.charAt(i);
            if (!Character.isWhitespace(currentChar)) {
                charArray[charCount++] = currentChar;
            }
        }
        if (charCount == originalSize) {
            return string;
        }
        return new String(charArray, 0, charCount);
    }
    
    
    /**
     * <p>
     * Internal utility method. DO NOT use this method directly.
     * </p>
     *
     * @param className the name of the class to be obtained
     * @return the obtained class
     * @throws ClassNotFoundException if class is not found
     */
    public static Class<?> getClass(final String className) throws ClassNotFoundException {
        
        final ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
        final ClassLoader loader = 
            (contextCL == null ? Utils.class.getClassLoader() : contextCL);
        
        if (primitiveClassAbbrevs.containsKey(className)) {
            final String clsName = "[" + primitiveClassAbbrevs.get(className);
            return Class.forName(clsName, true, loader).getComponentType();
        }
        return Class.forName(getClassCanonicalName(className), true, loader);

    }

    
    private static String getClassCanonicalName(final String className) {
        String trimmedClassName = Utils.removeAllWhitespaces(className); 
        if (trimmedClassName == null) {
            throw new IllegalArgumentException("className cannot be null");
        }
        if (trimmedClassName.endsWith("[]")) {
            final StringBuilder strBuilder = new StringBuilder();
            while (trimmedClassName.endsWith("[]")) {
                trimmedClassName = trimmedClassName.substring(0, trimmedClassName.length() - 2);
                strBuilder.append("[");
            }
            final String abbreviation = primitiveClassAbbrevs.get(trimmedClassName);
            if (abbreviation != null) {
                strBuilder.append(abbreviation);
            } else {
                strBuilder.append("L").append(trimmedClassName).append(";");
            }
            trimmedClassName = strBuilder.toString();
        }
        return trimmedClassName;
    }
    
    
}
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.Tree;
import org.javaruntype.exceptions.TypeRecognitionException;
import org.javaruntype.exceptions.TypeValidationException;
import org.javaruntype.type.parser.TypeLexer;
import org.javaruntype.type.parser.TypeParser;
import org.javaruntype.typedef.BoundedTypeDefVariable;
import org.javaruntype.typedef.InnerClassTypeDefVariable;
import org.javaruntype.typedef.InnerNamedTypeDefVariable;
import org.javaruntype.typedef.InnerParameterizedTypeTypeDefVariable;
import org.javaruntype.typedef.InnerTypeDefVariable;
import org.javaruntype.typedef.InnerWildcardTypeDefVariable;
import org.javaruntype.typedef.NamedTypeDefVariable;
import org.javaruntype.typedef.TypeDef;
import org.javaruntype.typedef.TypeDefVariable;
import org.javaruntype.typedef.TypeDefs;
import org.javaruntype.util.Utils;

                // If it is allowed, compute the arrayDimensions
                final Type<?> containedType = linkedTypeParameter.getType();
                int newArrayDimensions = 
                    containedType.getArrayDimensions() + 
                    innerVariable.getArrayDimensions();
                final Type<?> newType = 
                    typeRegistry.getTypeWithoutValidation(
                            containedType.getComponentClass(), 
                            containedType.getTypeParametersArray(), 
                            newArrayDimensions);
                
                typeParameters[i] = new StandardTypeParameter(newType);
                
            } else if (innerVariables[i] instanceof InnerWildcardTypeDefVariable) {
                
                final InnerWildcardTypeDefVariable wildcardVariable = 
                    (InnerWildcardTypeDefVariable) innerVariables[i];

                if (wildcardVariable.isUnbound()) {
                    
                    typeParameters[i] = WildcardTypeParameter.UNKNOWN;
                    
                } else {

                    /*
                     * We need to recursively obtain the inner type
                     */
                    
                    InnerTypeDefVariable bound = null;
                    if (wildcardVariable.hasUpperBound()) {
                        bound = wildcardVariable.getUpperBound();
                    } else { // wildcardVariable.hasLowerBound() == true
                        bound = wildcardVariable.getLowerBound();
                    }
                    
                    final TypeParameter<?>[] resolvedTypeParameters = 
                        resolveInnerParameterizedType(checkedTypeParametersByName,
                                currentVariableName, currentTypeParameter,
                                new InnerTypeDefVariable[] { bound });

                    if (resolvedTypeParameters == null) {
                        return null;
                    }
                    
                    if (resolvedTypeParameters.length != 1) {
                        throw new IllegalStateException("Wildcard variable " +
                                "is supposed to have a resolvable upper bound");
                    }
                    if (!(resolvedTypeParameters[0] instanceof StandardTypeParameter<?>)) {
                        throw new IllegalStateException("Wildcard variable " +
                                "is supposed to have a resolvable upper bound " +
                                "in the form of a Standard Type parameter");
                    }
                    
                    final Type<?> type = 
                        ((StandardTypeParameter<?>) resolvedTypeParameters[0]).getType();
                    
                    if (wildcardVariable.hasUpperBound()) {
                        typeParameters[i] = new ExtendsTypeParameter(type);
                    } else { // unknownVariable.hasLowerBound() == true
                        typeParameters[i] = new SuperTypeParameter(type);
                    }
                    
                }
                
            } else { // innerVariables[i] instanceof InnerParameterizedTypeTypeDefVariable
                
                final InnerParameterizedTypeTypeDefVariable parameterizedVariable =
                    (InnerParameterizedTypeTypeDefVariable) innerVariables[i];

                final TypeParameter<?>[] innerTypeParameters = 
                    resolveInnerParameterizedType(checkedTypeParametersByName,
                            currentVariableName, currentTypeParameter,
                            parameterizedVariable.getVariables());

                if (innerTypeParameters == null) {
                    // We have tried to solve a non-existing variable
                    return null;
                }
                
                final Class<?> componentClass = 
                    parameterizedVariable.getComponentClass();
                final int arrayDimensions = parameterizedVariable.getArrayDimensions();
                
                final Type<?> parameterizedType =
                    typeRegistry.getTypeWithoutValidation(
                            componentClass, innerTypeParameters, arrayDimensions);
                
                typeParameters[i] = new StandardTypeParameter(parameterizedType);
                
            }
            
        }
        return typeParameters;
    }


    
    
    
    
    
    static Type<?> getRawTypeForClass(final Class<?> typeClass) {
        return getRawTypeForClass(typeClass, 0);
    }
  
    
    
    private static Type<?> getRawTypeForClass(
            final Class<?> typeClass, final int arrayDimensions) {
        
        Class<?> componentClass = typeClass;
        int newArrayDimensions = arrayDimensions;
        
        while (componentClass.isArray()) {
            componentClass = componentClass.getComponentType();
            newArrayDimensions++;
        }
        
        final TypeDef typeDef = TypeDefs.forClass(componentClass);