import java.util.*;

/**
 <PRE>
 RFC 822
 
     3.3.  LEXICAL TOKENS

     The following rules are used to define an underlying lexical
     analyzer,  which  feeds  tokens to higher level parsers.  See the
     ANSI references, in the Bibliography.

                                                 ; (  Octal, Decimal.)
     CHAR        =  <any ASCII character>        ; (  0-177,  0.-127.)
     ALPHA       =  <any ASCII alphabetic character>
                                                 ; (101-132, 65.- 90.)
                                                 ; (141-172, 97.-122.)
     DIGIT       =  <any ASCII decimal digit>    ; ( 60- 71, 48.- 57.)
     CTL         =  <any ASCII control           ; (  0- 37,  0.- 31.)
                     character and DEL>          ; (    177,     127.)
     CR          =  <ASCII CR, carriage return>  ; (     15,      13.)
     LF          =  <ASCII LF, linefeed>         ; (     12,      10.)
     SPACE       =  <ASCII SP, space>            ; (     40,      32.)
     HTAB        =  <ASCII HT, horizontal-tab>   ; (     11,       9.)
     <">         =  <ASCII quote mark>           ; (     42,      34.)
     CRLF        =  CR LF

     LWSP-char   =  SPACE / HTAB                 ; semantics = SPACE

     linear-white-space =  1*([CRLF] LWSP-char)  ; semantics = SPACE
                                                 ; CRLF => folding
     specials    =  "(" / ")" / "<" / ">" / "@"  ; Must be in quoted-
                 /  "," / ";" / ":" / "\" / <">  ;  string, to use
                 /  "." / "[" / "]"              ;  within a word.
     delimiters  =  specials / linear-white-space / comment
     text        =  <any CHAR, including bare    ; => atoms, specials,
                     CR & bare LF, but NOT       ;  comments and
                     including CRLF>             ;  quoted-strings are
                                                 ;  NOT recognized.
     atom        =  1*<any CHAR except specials, SPACE and CTLs>

     quoted-string = <"> *(qtext/quoted-pair) <">; Regular qtext or
                                                 ;   quoted chars.
     qtext       =  <any CHAR excepting <">,     ; => may be folded
                     "\" & CR, and including
                     linear-white-space>
     domain-literal =  "[" *(dtext / quoted-pair) "]"

     dtext       =  <any CHAR excluding "[",     ; => may be folded
                     "]", "\" & CR, & including
                     linear-white-space>
     comment     =  "(" *(ctext / quoted-pair / comment) ")"
     ctext       =  <any CHAR excluding "(",     ; => may be folded
                     ")", "\" & CR, & including
                     linear-white-space>
     quoted-pair =  "\" CHAR                     ; may quote any char
     phrase      =  1*word                       ; Sequence of words
     word        =  atom / quoted-string
     
	ADDRESS SPECIFICATION

     6.1.  SYNTAX

     address     =  mailbox                      ; one addressee
                 /  group                        ; named list
     group       =  phrase ":" [#mailbox] ";"
     mailbox     =  addr-spec                    ; simple address
                 /  phrase route-addr            ; name & addr-spec
     route-addr  =  "<" [route] addr-spec ">"
     route       =  1#("@" domain) ":"           ; path-relative

     addr-spec   =  local-part "@" domain        ; global address
     local-part  =  word *("." word)             ; uninterpreted
 	                                             ; case-preserved
     domain      =  sub-domain *("." sub-domain)
     sub-domain  =  domain-ref / domain-literal

     domain-ref  =  atom                         ; symbolic reference

	In plain English, an e-mail address can look like this:
	
		(0) John
		(1) John.Doe
		(2) John@123.456.789
		(3) John.Doe@123.456.789
		(4) John.Doe@myhost
		(5) John.Doe@myhost.mynet
		(6) John@myhost
		(7) John@myhost.mynet 
		
		
	 
	Of course, the more likely scenario is that you would want e-mail addresses to be globally
	accessed addresses, so (0), (1), (4) and (6) would not be valid.
	Thus, this class will report e-mail addresses conforming to (0), (1), (4) and (6) as incorrectly
	formatted e-mail addresses.
	
	If you read the whole spec, there is a lot more to the address spec than what I specified above.
	For this version I am implementing the checking for the common use of an e-mail address:
	"user@host.net" type of e-mail. And I will leave full checking (according to the spec) for
	another version.
	
	*****************************************************************************
	
	EmailNameChecker - validation of e-mail addresses
	 
	Copyright (C) 1999  Shazron Abdullah
	  
	 * This library is free software; you can redistribute it and/or
	 * modify it under the terms of the GNU Lesser General Public
	 * License as published by the Free Software Foundation; either
	 * version 2.1 of the License, or (at your option) any later version.
	 * 
	 * This library is distributed in the hope that it will be useful,
	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	 * Lesser General Public License for more details.
	 * 
	 * You should have received a copy of the GNU Lesser General Public
	 * License along with this library; if not, write to the Free Software
	 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
	
	
	@author Shazron Abdullah
	@since JDK1.1.8
	@version 1.0 October 1999

*/
public class EmailNameChecker
{
	public static boolean isValidEmailAddress( String address )
	{
		return isValidCommonEmailAddress( address );
	}
	
	private static boolean isValidCommonEmailAddress( String address )
	{
		// Algorithm:
		// Check for existence of the @ symbol. If false, return false, else proceed.
		// Separate the two tokens on the left hand and the right hand side of the last 
		// occurence of the@ symbol.
		// Check that the user name part ( left hand side of the @ ) is correct.
		// Check that the machine name part ( right hand side of the @ ) is correct.
		// If both are true, return true, else return false.
		
		int symbol_index = address.lastIndexOf( '@' );
		if ( symbol_index == -1 ) { // symbol not found
			return false;
		}
		
		String user = address.substring(0,symbol_index);
		String machine_name = address.substring(symbol_index+1);
		
		return ( isValidUserName(user) && DomainNameChecker.isLegalDomainName( machine_name ) );
	}
	
	private static boolean isValidUserName( String user )
	{
		// Algorithm
		// Separate the different tokens between the periods
		// Check that the tokens are valid. ( Invalid characters are ()<>@,;:.[]\" )
		final String ILLEGAL_CHARS = "()<>@,;:.[]\\\"";
		boolean retVal = true;
		
		StringTokenizer strtok = new StringTokenizer( user, "." );
		while ( strtok.hasMoreTokens() ) {
			if ( DomainNameChecker.containsCharsInSet( strtok.nextToken(), ILLEGAL_CHARS ) ) {	
				retVal=false;
				break;
			}
		}

		return retVal;
	}
	
	// Testing
	public static void main(String args[])
	{
		String valid_address = "foo.bar@host.net";
		String valid_address2 = "foo@host.net";
		String invalid_address = "fo[>o.bar@host.net";
		String invalid_address2 = "foo";
				
		System.out.println( valid_address   + " : " + EmailNameChecker.isValidEmailAddress( valid_address ));
		System.out.println( valid_address2   + " : " + EmailNameChecker.isValidEmailAddress( valid_address2 ));
		System.out.println( invalid_address + " : " + EmailNameChecker.isValidEmailAddress( invalid_address ));
		System.out.println( invalid_address2 + " : " + EmailNameChecker.isValidEmailAddress( invalid_address2 ));
	}

}
