/*@bgen(jjtree) Generated By:JJTree: Do not edit this line. /Users/jason/Projects/apache-mime4j-0.3/target/generated-sources/jjtree/org/apache/james/mime4j/field/address/parser/AddressListParser.jj */
/*@egen*//****************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one   *
 * or more contributor license agreements.  See the NOTICE file *
 * distributed with this work for additional information        *
 * regarding copyright ownership.  The ASF licenses this file   *
 * to you under the Apache License, Version 2.0 (the            *
 * "License"); you may not use this file except in compliance   *
 * with the License.  You may obtain a copy of the License at   *
 *                                                              *
 *   http://www.apache.org/licenses/LICENSE-2.0                 *
 *                                                              *
 * Unless required by applicable law or agreed to in writing,   *
 * software distributed under the License is distributed on an  *
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
 * KIND, either express or implied.  See the License for the    *
 * specific language governing permissions and limitations      *
 * under the License.                                           *
 ****************************************************************/


/**
 * RFC2822 address list parser.
 *
 * Created 9/17/2004
 * by Joe Cheng <code@joecheng.com>
 */

options {
	STATIC=false;
	LOOKAHEAD=1;                                                                                                                               
	//DEBUG_PARSER=true;
	//DEBUG_TOKEN_MANAGER=true;
}

PARSER_BEGIN(AddressListParser)
/*
 *  Copyright 2004 the mime4j project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.apache.james.mime4j.field.address.parser;

public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeConstants/*@egen*/ {/*@bgen(jjtree)*/
  protected JJTAddressListParserState jjtree = new JJTAddressListParserState();

/*@egen*/
    public static void main(String args[]) throws ParseException {
		while (true) {
		    try {
				AddressListParser parser = new AddressListParser(System.in);
		    	parser.parseLine();
		    	((SimpleNode)parser.jjtree.rootNode()).dump("> ");
		    } catch (Exception x) {
				x.printStackTrace();
				return;
		    }
		}
    }
    
    private static void log(String msg) {
    	System.out.print(msg);
    }
    
    public ASTaddress_list parse() throws ParseException {
        try {
    	    parseAll();
    	    return (ASTaddress_list)jjtree.rootNode();
    	} catch (TokenMgrError tme) {
    	    throw new ParseException(tme.getMessage());
    	}
    }
    
    
    void jjtreeOpenNodeScope(Node n) {
    	((SimpleNode)n).firstToken = getToken(1);
    }
    
    void jjtreeCloseNodeScope(Node n) {
    	((SimpleNode)n).lastToken = getToken(0);
    }
}

PARSER_END(AddressListParser)

void parseLine()       :
{}
{
	address_list() ["\r"] "\n"
}

void parseAll()       :
{}
{
	address_list() <EOF>
}

void address_list() :
{/*@bgen(jjtree) address_list */
  ASTaddress_list jjtn000 = new ASTaddress_list(JJTADDRESS_LIST);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/}
{/*@bgen(jjtree) address_list */
        try {
/*@egen*/
	[ address() ]
	(
		","
		[ address() ]
	)*/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

void address() :
{/*@bgen(jjtree) address */
  ASTaddress jjtn000 = new ASTaddress(JJTADDRESS);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/}
{/*@bgen(jjtree) address */
        try {
/*@egen*/
	LOOKAHEAD(2147483647)
	addr_spec()
|	angle_addr()
|	( phrase() (group_body() | angle_addr()) )/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

void mailbox() :
{/*@bgen(jjtree) mailbox */
  ASTmailbox jjtn000 = new ASTmailbox(JJTMAILBOX);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/}
{/*@bgen(jjtree) mailbox */
        try {
/*@egen*/
	LOOKAHEAD(2147483647)
	addr_spec()
|	angle_addr()
|	name_addr()/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

void name_addr() :
{/*@bgen(jjtree) name_addr */
  ASTname_addr jjtn000 = new ASTname_addr(JJTNAME_ADDR);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/}
{/*@bgen(jjtree) name_addr */
        try {
/*@egen*/
	phrase() angle_addr()/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

void group_body() :
{/*@bgen(jjtree) group_body */
  ASTgroup_body jjtn000 = new ASTgroup_body(JJTGROUP_BODY);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/}
{/*@bgen(jjtree) group_body */
        try {
/*@egen*/
	":"
	[ mailbox() ]
	(
		","
		[ mailbox() ]
	)*
	";"/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

void angle_addr() :
{/*@bgen(jjtree) angle_addr */
  ASTangle_addr jjtn000 = new ASTangle_addr(JJTANGLE_ADDR);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/}
{/*@bgen(jjtree) angle_addr */
        try {
/*@egen*/
	"<" [ route() ] addr_spec() ">"/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

void route() :
{/*@bgen(jjtree) route */
  ASTroute jjtn000 = new ASTroute(JJTROUTE);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/}
{/*@bgen(jjtree) route */
        try {
/*@egen*/
	"@" domain() ( (",")* "@" domain() )* ":"/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

void phrase() :
{/*@bgen(jjtree) phrase */
  ASTphrase jjtn000 = new ASTphrase(JJTPHRASE);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/}
{/*@bgen(jjtree) phrase */
try {
/*@egen*/
(	<DOTATOM>
|	<QUOTEDSTRING>
)+/*@bgen(jjtree)*/
} finally {
  if (jjtc000) {
    jjtree.closeNodeScope(jjtn000, true);
    jjtreeCloseNodeScope(jjtn000);
  }
}
/*@egen*/
}

void addr_spec() :
{/*@bgen(jjtree) addr_spec */
  ASTaddr_spec jjtn000 = new ASTaddr_spec(JJTADDR_SPEC);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/}
{/*@bgen(jjtree) addr_spec */
        try {
/*@egen*/
	( local_part() "@" domain() )/*@bgen(jjtree)*/
        } catch (Throwable jjte000) {
          if (jjtc000) {
            jjtree.clearNodeScope(jjtn000);
            jjtc000 = false;
          } else {
            jjtree.popNode();
          }
          if (jjte000 instanceof RuntimeException) {
            throw (RuntimeException)jjte000;
          }
          if (jjte000 instanceof ParseException) {
            throw (ParseException)jjte000;
          }
          throw (Error)jjte000;
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

void local_part() :
{/*@bgen(jjtree) local_part */
  ASTlocal_part jjtn000 = new ASTlocal_part(JJTLOCAL_PART);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/ Token t; }
{/*@bgen(jjtree) local_part */
        try {
/*@egen*/
	( t=<DOTATOM> | t=<QUOTEDSTRING> )
	(	[t="."]
		{
			if (t.image.charAt(t.image.length() - 1) != '.' || t.kind == AddressListParserConstants.QUOTEDSTRING)
				throw new ParseException("Words in local part must be separated by '.'");
		}
		(	t=<DOTATOM> | t=<QUOTEDSTRING> )
	)*/*@bgen(jjtree)*/
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

void domain() :
{/*@bgen(jjtree) domain */
  ASTdomain jjtn000 = new ASTdomain(JJTDOMAIN);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
/*@egen*/ Token t; }
{/*@bgen(jjtree) domain */
        try {
/*@egen*/
	(	t=<DOTATOM>
		(	[t="."]
			{
				if (t.image.charAt(t.image.length() - 1) != '.')
					throw new ParseException("Atoms in domain names must be separated by '.'");
			}
			t=<DOTATOM>
		)*
	)
|	<DOMAINLITERAL>/*@bgen(jjtree)*/
        } finally {
          if (jjtc000) {
            jjtree.closeNodeScope(jjtn000, true);
            jjtreeCloseNodeScope(jjtn000);
          }
        }
/*@egen*/
}

SPECIAL_TOKEN :
{
 	< WS: ( [" ", "\t"] )+ >
}

TOKEN :
{
	< #ALPHA: ["a" - "z", "A" - "Z"] >
|	< #DIGIT: ["0" - "9"] >
|	< #ATEXT: ( <ALPHA> | <DIGIT>
			  | "!" | "#" | "$" | "%"
			  | "&" | "'" | "*" | "+"
			  | "-" | "/" | "=" | "?"
			  | "^" | "_" | "`" | "{"
			  | "|" | "}" | "~"
			  )>
|	< DOTATOM: <ATEXT> ( <ATEXT> | "." )* >
}

TOKEN_MGR_DECLS :
{
	// Keeps track of how many levels of comment nesting
	// we've encountered.  This is only used when the 2nd
	// level is reached, for example ((this)), not (this).
	// This is because the outermost level must be treated
	// specially anyway, because the outermost ")" has a 
	// different token type than inner ")" instances.
	static int commentNest;
}

MORE :
{
	// domain literal
	"[" : INDOMAINLITERAL
}

<INDOMAINLITERAL>
MORE :
{
	< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
|	< ~["[", "]", "\\"] >
}

<INDOMAINLITERAL>
TOKEN :
{
	< DOMAINLITERAL: "]" > { matchedToken.image = image.toString(); }: DEFAULT
}

MORE :
{
	// starts a comment
	"(" : INCOMMENT
}

<INCOMMENT>
SKIP :
{
	// ends a comment
	< COMMENT: ")" > : DEFAULT
	// if this is ever changed to not be a SKIP, need
	// to make sure matchedToken.token = token.toString()
	// is called.
}

<INCOMMENT>
MORE :
{
	< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
|	"(" { commentNest = 1; } : NESTED_COMMENT
|	< <ANY>>
}

<NESTED_COMMENT>
MORE :
{
	< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
|	"(" { ++commentNest; }
|	")" { --commentNest; if (commentNest == 0) SwitchTo(INCOMMENT); }
|	< <ANY>>
}


// QUOTED STRINGS

MORE :
{
	"\"" { image.deleteCharAt(image.length() - 1); } : INQUOTEDSTRING
}

<INQUOTEDSTRING>
MORE :
{
	< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
|	< (~["\"", "\\"])+ >
}

<INQUOTEDSTRING>
TOKEN :
{
	< QUOTEDSTRING: "\"" > { matchedToken.image = image.substring(0, image.length() - 1); } : DEFAULT
}

// GLOBALS

<*>
TOKEN :
{
	< #QUOTEDPAIR: "\\" <ANY> >
|	< #ANY: ~[] >
}

// ERROR!
/*

<*>
TOKEN :
{
	< UNEXPECTED_CHAR: <ANY> >
}

*/