Update for Jakarta EE 8 compliancy

git-svn-id: https://svn.apache.org/repos/asf/geronimo/specs/trunk@1864210 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/geronimo-javamail_1.5_spec/pom.xml b/geronimo-javamail_1.5_spec/pom.xml
index 58def12..5d514fc 100644
--- a/geronimo-javamail_1.5_spec/pom.xml
+++ b/geronimo-javamail_1.5_spec/pom.xml
@@ -91,7 +91,7 @@
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
-            <version>3.8.2</version>
+            <version>4.12</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
@@ -100,6 +100,15 @@
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.6.1</version>
+                <configuration>
+                    <source>1.7</source>
+                    <target>1.7</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
                 <configuration>
                     <artifactSet>
diff --git a/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/HeaderTokenizer.java b/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/HeaderTokenizer.java
index 5359e8c..bc47f60 100644
--- a/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/HeaderTokenizer.java
+++ b/geronimo-javamail_1.5_spec/src/main/java/javax/mail/internet/HeaderTokenizer.java
@@ -79,18 +79,18 @@
     //Return the rest of the Header.
     //null is returned if we are already at end of header
     public String getRemainder() {
-        
+
         if(pos > _headerLength) {
             return null;
         }
-        
+
         return _header.substring(pos);
     }
 
     public Token next() throws ParseException {
         return readToken(NUL, false);
     }
-    
+
     /**
      * Parses the next token from this String.
      * If endOfAtom is not NUL, the token extends until the
@@ -123,10 +123,10 @@
      * @since       JavaMail 1.5
      */
     public Token next(final char endOfAtom, final boolean keepEscapes)
-                throws ParseException {        
+                throws ParseException {
         return readToken(endOfAtom, keepEscapes);
     }
-                
+
 
     public Token peek() throws ParseException {
         final int start = pos;
@@ -150,7 +150,7 @@
         while (++pos < _headerLength) {
             // break on the first non-atom character.
             final char ch = _header.charAt(pos);
-         
+
             if ((_delimiters.indexOf(_header.charAt(pos)) != -1 || ch < 32 || ch >= 127)) {
                 break;
             }
@@ -179,29 +179,33 @@
                 } else {
                     return comment;
                 }
-                // quoted literal
+
+            // quoted literal
             } else if (c == '\"') {
                 return readQuotedString('"', keepEscapes, 1);
+
             // white space, eat this and find a real token.
             } else if (WHITE.indexOf(c) != -1) {
                 eatWhiteSpace();
                 return readToken(endOfAtom, keepEscapes);
+
             // either a CTL or special.  These characters have a self-defining token type.
             } else if (c < 32 || c >= 127 || _delimiters.indexOf(c) != -1) {
-                
+
                 if (endOfAtom != NUL && c != endOfAtom) {
                     return readQuotedString(endOfAtom, keepEscapes, 0);
                 }
-                
-                
+
+
                 pos++;
                 return new Token(c, String.valueOf(c));
+
             } else {
                 // start of an atom, parse it off.
                 if (endOfAtom != NUL && c != endOfAtom) {
                     return readQuotedString(endOfAtom, keepEscapes, 0);
                 }
-                
+
                 return readAtomicToken();
             }
         }
@@ -228,11 +232,11 @@
                 if (i == end) {
                     throw new ParseException("Invalid escape character");
                 }
-                
+
                 if(keepEscapes) {
                     value.append("\\");
                 }
-                
+
                 value.append(_header.charAt(i));
             }
             // line breaks are ignored, except for naked '\n' characters, which are consider
@@ -244,7 +248,7 @@
                 }
             }
             else {
-                 
+
                  // just append the ch value.
                 value.append(ch);
             }
@@ -336,7 +340,21 @@
             }
         }
 
-        throw new ParseException("Missing '\"'");
+        // we ran out of chars in the string. If the end char is a quote, then there
+        // is a missing quote somewhere
+        if (endChar == '"') {
+            throw new ParseException("Missing '\"'");
+        }
+
+        // otherwise, we can just return whatever is left
+        String value;
+        if (requiresEscaping) {
+            value = getEscapedValue(start, pos, keepEscapes);
+
+        } else {
+            value = _header.substring(start, pos);
+        }
+        return new Token(Token.QUOTEDSTRING, trimWhiteSpace(value));
     }
 
     /**
@@ -346,7 +364,54 @@
         // skip to end of whitespace
         while (++pos < _headerLength
                 && WHITE.indexOf(_header.charAt(pos)) != -1) {
-			;
-		}
+            ;
+        }
     }
+
+    /**
+     * linear white spaces must be removed from quoted text or text
+     *
+     LWSP-char   =  SPACE / HTAB                 ; semantics = SPACE
+
+     linear-white-space =  1*([CRLF] LWSP-char)  ; semantics = SPACE
+                                                 ; CRLF => folding
+
+     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) "]"
+     */
+    private static String trimWhiteSpace(final String s) {
+        char c;
+        int i;
+        for (i = s.length() - 1; i >= 0; i--) {
+            if ((
+                    (c = s.charAt(i)) != ' ') && // space
+                    (c != '\t') &&              // tab
+                    (c != '\r') &&              // CR
+                    (c != '\n')) {              // LF
+
+                break;
+            }
+        }
+
+        if (i <= 0) {
+            return "";
+
+        } else {
+            return s.substring(0, i + 1);
+        }
+    }
+
 }
diff --git a/geronimo-javamail_1.5_spec/src/test/java/javax/mail/internet/HeaderTokenizerTest.java b/geronimo-javamail_1.5_spec/src/test/java/javax/mail/internet/HeaderTokenizerTest.java
index 13cbec3..7b87892 100644
--- a/geronimo-javamail_1.5_spec/src/test/java/javax/mail/internet/HeaderTokenizerTest.java
+++ b/geronimo-javamail_1.5_spec/src/test/java/javax/mail/internet/HeaderTokenizerTest.java
@@ -20,7 +20,6 @@
 package javax.mail.internet;
 
 import javax.mail.internet.HeaderTokenizer.Token;
-
 import junit.framework.TestCase;
 
 /**
@@ -28,10 +27,8 @@
  */
 public class HeaderTokenizerTest extends TestCase {
     public void testTokenizer() throws ParseException {
-        final Token t;
-        HeaderTokenizer ht;
-        ht =
-            new HeaderTokenizer("To: \"Geronimo List\" <geronimo-dev@apache.org>, \n\r Geronimo User <geronimo-user@apache.org>");
+
+        HeaderTokenizer ht = new HeaderTokenizer("To: \"Geronimo List\" <geronimo-dev@apache.org>, \n\r Geronimo User <geronimo-user@apache.org>");
         validateToken(ht.peek(), Token.ATOM, "To");
         validateToken(ht.next(), Token.ATOM, "To");
         validateToken(ht.peek(), ':', ":");
@@ -57,11 +54,14 @@
         validateToken(ht.next(), Token.ATOM, "org");
         validateToken(ht.next(), '>', ">");
         assertEquals(Token.EOF, ht.next().getType());
+
         ht = new HeaderTokenizer("   ");
         assertEquals(Token.EOF, ht.next().getType());
+
         ht = new HeaderTokenizer("J2EE");
         validateToken(ht.next(), Token.ATOM, "J2EE");
         assertEquals(Token.EOF, ht.next().getType());
+
         // test comments
         doComment(true);
         doComment(false);
@@ -95,23 +95,17 @@
     }
     
     public void testJavaMail15NextMethod() throws ParseException{
-        HeaderTokenizer ht;
-        ht =
-            new HeaderTokenizer("To: \"Geronimo List\\\" <geronimo-dev@apache.org>, \n\r Geronimo User <geronimo-user@apache.org>");
+        HeaderTokenizer ht = new HeaderTokenizer("To: \"Geronimo List\\\" <geronimo-dev@apache.org>, \n\r Geronimo User <geronimo-user@apache.org>");
         validateToken(ht.next('>', false), Token.QUOTEDSTRING, "To: \"Geronimo List\" <geronimo-dev@apache.org");
     }
     
     public void testJavaMail15NextMethodEscapes() throws ParseException{
-        HeaderTokenizer ht;
-        ht =
-            new HeaderTokenizer("To: \"Geronimo List\\\" <geronimo-dev@apache.org>, \n\r Geronimo User <geronimo-user@apache.org>");
+        HeaderTokenizer ht = new HeaderTokenizer("To: \"Geronimo List\\\" <geronimo-dev@apache.org>, \n\r Geronimo User <geronimo-user@apache.org>");
         validateToken(ht.next('<', true), Token.QUOTEDSTRING, "To: \"Geronimo List\\\" ");
     }
     
     public void testJavaMail15NextMethodEscapes2() throws ParseException{
-        HeaderTokenizer ht;
-        ht =
-            new HeaderTokenizer("To: \"Geronimo List\" <geronimo-dev@apac\\he.org>, \n\r Geronimo User <geronimo-user@apache.org>");
+        HeaderTokenizer ht = new HeaderTokenizer("To: \"Geronimo List\" <geronimo-dev@apac\\he.org>, \n\r Geronimo User <geronimo-user@apache.org>");
         ht.next();
         ht.next();
         ht.next();
@@ -119,9 +113,7 @@
     }
     
     public void testJavaMail15NextMethodEscapes3() throws ParseException{
-        HeaderTokenizer ht;
-        ht =
-            new HeaderTokenizer("To: \"Geronimo List\" <geronimo-dev@apac\\he.org>, \n\r Geronimo User <geronimo-user@apache.org>");
+        HeaderTokenizer ht = new HeaderTokenizer("To: \"Geronimo List\" <geronimo-dev@apac\\he.org>, \n\r Geronimo User <geronimo-user@apache.org>");
         ht.next();
         ht.next();
         ht.next();
@@ -129,18 +121,15 @@
     }
 
     public void checkTokenParse(final String text, final int type, final String value) throws ParseException {
-        HeaderTokenizer ht;
-        ht = new HeaderTokenizer(text, HeaderTokenizer.RFC822, false);
+        HeaderTokenizer ht = new HeaderTokenizer(text, HeaderTokenizer.RFC822, false);
         validateToken(ht.next(), type, value);
     }
 
 
     public void checkParseError(final String text) throws ParseException {
-        final Token t;
-        HeaderTokenizer ht;
-
-        ht = new HeaderTokenizer(text);
+        HeaderTokenizer ht = new HeaderTokenizer(text);
         doNextError(ht);
+
         ht = new HeaderTokenizer(text);
         doPeekError(ht);
     }
@@ -164,13 +153,12 @@
 
     public void doComment(final boolean ignore) throws ParseException {
         HeaderTokenizer ht;
-        final Token t;
-        ht =
-            new HeaderTokenizer(
+        ht = new HeaderTokenizer(
                 "Apache(Geronimo)J2EE",
                 HeaderTokenizer.RFC822,
                 ignore);
         validateToken(ht.next(), Token.ATOM, "Apache");
+
         if (!ignore) {
             validateToken(ht.next(), Token.COMMENT, "Geronimo");
         }
@@ -182,6 +170,8 @@
                 "Apache(Geronimo (Project))J2EE",
                 HeaderTokenizer.RFC822,
                 ignore);
+
+
         validateToken(ht.next(), Token.ATOM, "Apache");
         if (!ignore) {
             validateToken(ht.next(), Token.COMMENT, "Geronimo (Project)");
@@ -194,4 +184,122 @@
         assertEquals(type, token.getType());
         assertEquals(value, token.getValue());
     }
+
+    private transient TestCase[] testCases = { new TestCase(';', "a=b c", "b c"),
+            new TestCase(';', "a=b c; d=e f", "b c"),
+            new TestCase(';', "a=\"b c\"; d=e f", "b c") };
+
+    private transient TestCase[] testCasesEsc = {
+            new TestCase(';', "a=b \\c", "b \\c"),
+            new TestCase(';', "a=b c; d=e f", "b c"),
+            new TestCase(';', "a=\"b \\c\"; d=e f", "b \\c") };
+
+    public void testNext() throws Exception {
+
+        final String value = "ggere, /tmp/mail.out, +mailbox, ~user/mailbox, ~/mailbox, /PN=x400.address/PRMD=ibmmail/ADMD=ibmx400/C=us/@mhs-mci.ebay, " +
+                "C'est bien moche <paris@france>, Mad Genius <george@boole>, two@weeks (It Will Take), /tmp/mail.out, laborious (But Bug Free), " +
+                "cannot@waste (My, Intellectual, Cycles), users:get,what,they,deserve;, it (takes, no (time, at) all), " +
+                "if@you (could, see (almost, as, (badly, you) would) agree), famous <French@physicists>, " +
+                "it@is (brilliant (genius, and) superb), confused (about, being, french)";
+
+        // Create HeaderTokenizer object
+        HeaderTokenizer ht = new HeaderTokenizer(value,
+                HeaderTokenizer.RFC822,
+                true);
+
+        HeaderTokenizer.Token token;
+
+        while ((token = ht.next()).getType() != HeaderTokenizer.Token.EOF) { // API
+            if (token.getType() == 0 || token.getValue() == null) {
+                fail(type(token.getType()) + "\t" + token.getValue());
+            }
+        }
+    }
+
+    public void testNext2() throws Exception {
+
+        // Create HeaderTokenizer object
+        HeaderTokenizer ht;
+        HeaderTokenizer.Token token;
+
+        for (TestCase tc : testCases) {
+            ht = new HeaderTokenizer(tc.test, HeaderTokenizer.MIME, true);
+            token = ht.next();
+            if (token.getType() != HeaderTokenizer.Token.ATOM || !token.getValue().equals("a")) {
+                System.out.println("\t" + type(token.getType()) + "\t" + token.getValue());
+                fail();
+
+            } else {
+                token = ht.next();
+                if (token.getType() != '=') {
+                    System.out.println("\t" + type(token.getType()) + "\t" + token.getValue());
+                    fail();
+                } else {
+                    token = ht.next(tc.endOfAtom);
+                    if (token.getType() != HeaderTokenizer.Token.QUOTEDSTRING
+                            || !token.getValue().equals(tc.expected)) {
+                        fail(type(token.getType()) + "\t" + token.getValue());
+                    }
+                }
+            }
+        }
+    }
+
+    public void testNext3() throws Exception {
+
+        // Create HeaderTokenizer object
+        HeaderTokenizer ht;
+        HeaderTokenizer.Token token;
+
+        for (TestCase tc : testCasesEsc) {
+            ht = new HeaderTokenizer(tc.test, HeaderTokenizer.MIME, true);
+            token = ht.next();
+            if (token.getType() != HeaderTokenizer.Token.ATOM || !token.getValue().equals("a")) {
+                System.out.println("\t" + type(token.getType()) + "\t" + token.getValue());
+                fail();
+            } else {
+                token = ht.next();
+                if (token.getType() != '=') {
+                    System.out.println("\t" + type(token.getType()) + "\t" + token.getValue());
+                    fail();
+                } else {
+                    token = ht.next(tc.endOfAtom, true);
+                    if (token.getType() != HeaderTokenizer.Token.QUOTEDSTRING
+                            || !token.getValue().equals(tc.expected)) {
+                        fail(type(token.getType()) + "\t" + token.getValue());
+                    }
+                }
+            }
+        }
+
+    }
+
+    private static String type(int t) {
+        if (t == HeaderTokenizer.Token.ATOM)
+            return "ATOM";
+        else if (t == HeaderTokenizer.Token.QUOTEDSTRING)
+            return "QUOTEDSTRING";
+        else if (t == HeaderTokenizer.Token.COMMENT)
+            return "COMMENT";
+        else if (t == HeaderTokenizer.Token.EOF)
+            return "EOF";
+        else if (t < 0)
+            return "UNKNOWN";
+        else
+            return "SPECIAL";
+    }
+
+    static class TestCase {
+
+        public TestCase(final char endOfAtom, final String test, String expected) {
+            this.endOfAtom = endOfAtom;
+            this.test = test;
+            this.expected = expected;
+        }
+
+        public char endOfAtom;
+        public String test;
+        public String expected;
+    };
+    
 }