/**************************************************************** * 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 */ options { STATIC=false; LOOKAHEAD=1; JDK_VERSION = "1.5"; VISITOR=true; MULTI=true; NODE_SCOPE_HOOK=true; NODE_EXTENDS="org.apache.james.mime4j.field.address.BaseNode"; //DEBUG_PARSER=true; //DEBUG_TOKEN_MANAGER=true; } PARSER_BEGIN(AddressListParser) /**************************************************************** * 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. * ****************************************************************/ package org.apache.james.mime4j.field.address; public class AddressListParser { 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; } } } public ASTaddress_list parseAddressList() throws ParseException { try { parseAddressList0(); return (ASTaddress_list) jjtree.rootNode(); } catch (TokenMgrError tme) { throw new ParseException(tme.getMessage()); } } public ASTaddress parseAddress() throws ParseException { try { parseAddress0(); return (ASTaddress) jjtree.rootNode(); } catch (TokenMgrError tme) { throw new ParseException(tme.getMessage()); } } public ASTmailbox parseMailbox() throws ParseException { try { parseMailbox0(); return (ASTmailbox) 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() #void : {} { address_list() ["\r"] "\n" } void parseAddressList0() #void : {} { address_list() } void parseAddress0() #void : {} { address() } void parseMailbox0() #void : {} { mailbox() } void address_list() : {} { [ address() ] ( "," [ address() ] )* } void address() : {} { LOOKAHEAD(2147483647) addr_spec() | angle_addr() | ( phrase() (group_body() | angle_addr()) ) } void mailbox() : {} { LOOKAHEAD(2147483647) addr_spec() | angle_addr() | name_addr() } void name_addr() : {} { phrase() angle_addr() } void group_body() : {} { ":" [ mailbox() ] ( "," [ mailbox() ] )* ";" } void angle_addr() : {} { "<" [ route() ] addr_spec() ">" } void route() : {} { "@" domain() ( (",")* "@" domain() )* ":" } void phrase() : {} { ( | )+ } void addr_spec() : {} { ( local_part() "@" domain() ) } void local_part() : { Token t; } { ( t= | t= ) ( [t="."] { if ( t.kind == AddressListParserConstants.QUOTEDSTRING || t.image.charAt(t.image.length() - 1) != '.') throw new ParseException("Words in local part must be separated by '.'"); } ( t= | t= ) )* } void domain() : { Token t; } { ( t= ( [t="."] { if (t.image.charAt(t.image.length() - 1) != '.') throw new ParseException("Atoms in domain names must be separated by '.'"); } t= )* ) | } SPECIAL_TOKEN : { < WS: ( [" ", "\t"] )+ > } TOKEN : { < #ALPHA: ["a" - "z", "A" - "Z"] > | < #DIGIT: ["0" - "9"] > | < #ATEXT: ( | | "!" | "#" | "$" | "%" | "&" | "'" | "*" | "+" | "-" | "/" | "=" | "?" | "^" | "_" | "`" | "{" | "|" | "}" | "~" )> | < DOTATOM: ( | "." )* > } 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 } MORE : { < > { image.deleteCharAt(image.length() - 2); } | < ~["[", "]", "\\"] > } TOKEN : { < DOMAINLITERAL: "]" > { matchedToken.image = image.toString(); }: DEFAULT } MORE : { // starts a comment "(" : 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. } MORE : { < > { image.deleteCharAt(image.length() - 2); } | "(" { commentNest = 1; } : NESTED_COMMENT | < > } MORE : { < > { image.deleteCharAt(image.length() - 2); } | "(" { ++commentNest; } | ")" { --commentNest; if (commentNest == 0) SwitchTo(INCOMMENT); } | < > } // QUOTED STRINGS MORE : { "\"" { image.deleteCharAt(image.length() - 1); } : INQUOTEDSTRING } MORE : { < > { image.deleteCharAt(image.length() - 2); } | < (~["\"", "\\"])+ > } TOKEN : { < QUOTEDSTRING: "\"" > { matchedToken.image = image.substring(0, image.length() - 1); } : DEFAULT } // GLOBALS <*> TOKEN : { < #QUOTEDPAIR: "\\" > | < #ANY: ~[] > } // ERROR! /* <*> TOKEN : { < UNEXPECTED_CHAR: > } */