You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by rd...@apache.org on 2008/02/28 23:37:28 UTC

svn commit: r632141 - in /james/server/trunk: imap-api/src/main/java/org/apache/james/api/imap/message/request/ imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/imap4rev1/ imap-codec-library/src/test/java/org/apache/james/imaps...

Author: rdonkin
Date: Thu Feb 28 14:37:25 2008
New Revision: 632141

URL: http://svn.apache.org/viewvc?rev=632141&view=rev
Log:
Completed search parsing.

Added:
    james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserAndParenthesesTest.java
    james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserTopLevelAndTest.java
Modified:
    james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/request/SearchKey.java
    james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParser.java
    james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserOrTest.java
    james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserSearchKeySequenceSetTest.java

Modified: james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/request/SearchKey.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/request/SearchKey.java?rev=632141&r1=632140&r2=632141&view=diff
==============================================================================
--- james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/request/SearchKey.java (original)
+++ james/server/trunk/imap-api/src/main/java/org/apache/james/api/imap/message/request/SearchKey.java Thu Feb 28 14:37:25 2008
@@ -1,6 +1,8 @@
 package org.apache.james.api.imap.message.request;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 import org.apache.james.api.imap.message.IdRange;
 
@@ -54,57 +56,59 @@
     public static final int TYPE_NOT = 35;
     // OR
     public static final int TYPE_OR = 36;
+    // AND
+    public static final int TYPE_AND = 36;
     
     private static final SearchKey UNSEEN = new SearchKey(TYPE_UNSEEN,
-            null, null, null, 0, null, null, null);
+            null, null, 0, null, null, null);
 
     private static final SearchKey UNFLAGGED = new SearchKey(
-            TYPE_UNFLAGGED, null, null, null, 0, null, null, null);
+            TYPE_UNFLAGGED, null, null, 0, null, null, null);
 
     private static final SearchKey UNDRAFT = new SearchKey(TYPE_UNDRAFT,
-            null, null, null, 0, null, null, null);
+            null, null, 0, null, null, null);
 
     private static final SearchKey UNDELETED = new SearchKey(
-            TYPE_UNDELETED, null, null, null, 0, null, null, null);
+            TYPE_UNDELETED, null, null, 0, null, null, null);
 
     private static final SearchKey UNANSWERED = new SearchKey(
-            TYPE_UNANSWERED, null, null, null, 0, null, null, null);
+            TYPE_UNANSWERED, null, null, 0, null, null, null);
 
     private static final SearchKey SEEN = new SearchKey(TYPE_SEEN, null,
-            null, null, 0, null, null, null);
+            null, 0, null, null, null);
 
     private static final SearchKey RECENT = new SearchKey(TYPE_RECENT,
-            null, null, null, 0, null, null, null);
+            null, null, 0, null, null, null);
 
     private static final SearchKey OLD = new SearchKey(TYPE_OLD, null,
-            null, null, 0, null, null, null);
+            null, 0, null, null, null);
 
     private static final SearchKey NEW = new SearchKey(TYPE_NEW, null,
-            null, null, 0, null, null, null);
+            null, 0, null, null, null);
 
     private static final SearchKey FLAGGED = new SearchKey(TYPE_FLAGGED,
-            null, null, null, 0, null, null, null);
+            null, null, 0, null, null, null);
 
     private static final SearchKey DRAFT = new SearchKey(TYPE_DRAFT, null,
-            null, null, 0, null, null, null);
+            null, 0, null, null, null);
 
     private static final SearchKey DELETED = new SearchKey(TYPE_DELETED,
-            null, null, null, 0, null, null, null);
+            null,null, 0, null, null, null);
 
     private static final SearchKey ANSWERED = new SearchKey(TYPE_ANSWERED,
-            null, null, null, 0, null, null, null);
+            null, null, 0, null, null, null);
 
     private static final SearchKey ALL = new SearchKey(TYPE_ALL, null,
-            null, null, 0, null, null, null);
+            null, 0, null, null, null);
 
     // NUMBERS
     public static SearchKey buildSequenceSet(IdRange[] ids) {
-        return new SearchKey(TYPE_SEQUENCE_SET, null, null, null, 0, null,
+        return new SearchKey(TYPE_SEQUENCE_SET, null, null, 0, null,
                 null, ids);
     }
 
     public static SearchKey buildUidSet(IdRange[] ids) {
-        return new SearchKey(TYPE_UID, null, null, null, 0, null, null, ids);
+        return new SearchKey(TYPE_UID, null, null, 0, null, null, ids);
     }
 
     // NO PARAMETERS
@@ -166,125 +170,139 @@
 
     // ONE VALUE
     public static SearchKey buildBcc(String value) {
-        return new SearchKey(TYPE_BCC, null, null, null, 0, null, value,
+        return new SearchKey(TYPE_BCC, null, null, 0, null, value,
                 null);
     }
 
     public static SearchKey buildBody(String value) {
-        return new SearchKey(TYPE_BODY, null, null, null, 0, null, value,
+        return new SearchKey(TYPE_BODY, null, null, 0, null, value,
                 null);
     }
 
     public static SearchKey buildCc(String value) {
-        return new SearchKey(TYPE_CC, null, null, null, 0, null, value,
+        return new SearchKey(TYPE_CC, null, null, 0, null, value,
                 null);
     }
 
     public static SearchKey buildFrom(String value) {
-        return new SearchKey(TYPE_FROM, null, null, null, 0, null, value,
+        return new SearchKey(TYPE_FROM, null, null, 0, null, value,
                 null);
     }
 
     public static SearchKey buildKeyword(String value) {
-        return new SearchKey(TYPE_KEYWORD, null, null, null, 0, null,
+        return new SearchKey(TYPE_KEYWORD, null, null, 0, null,
                 value, null);
     }
 
     public static SearchKey buildSubject(String value) {
-        return new SearchKey(TYPE_SUBJECT, null, null, null, 0, null,
+        return new SearchKey(TYPE_SUBJECT, null, null, 0, null,
                 value, null);
     }
 
     public static SearchKey buildText(String value) {
-        return new SearchKey(TYPE_TEXT, null, null, null, 0, null, value,
+        return new SearchKey(TYPE_TEXT, null, null, 0, null, value,
                 null);
     }
 
     public static SearchKey buildTo(String value) {
-        return new SearchKey(TYPE_TO, null, null, null, 0, null, value,
+        return new SearchKey(TYPE_TO, null, null, 0, null, value,
                 null);
     }
 
     public static SearchKey buildUnkeyword(String value) {
-        return new SearchKey(TYPE_UNKEYWORD, null, null, null, 0, null,
+        return new SearchKey(TYPE_UNKEYWORD, null, null, 0, null,
                 value, null);
     }
 
     // ONE DATE
     public static SearchKey buildBefore(DayMonthYear date) {
-        return new SearchKey(TYPE_BEFORE, date, null, null, 0, null, null,
+        return new SearchKey(TYPE_BEFORE, date, null, 0, null, null,
                 null);
     }
 
     public static SearchKey buildOn(DayMonthYear date) {
-        return new SearchKey(TYPE_ON, date, null, null, 0, null, null, null);
+        return new SearchKey(TYPE_ON, date, null, 0, null, null, null);
     }
 
     public static SearchKey buildSentBefore(DayMonthYear date) {
-        return new SearchKey(TYPE_SENTBEFORE, date, null, null, 0, null,
+        return new SearchKey(TYPE_SENTBEFORE, date, null, 0, null,
                 null, null);
     }
 
     public static SearchKey buildSentOn(DayMonthYear date) {
-        return new SearchKey(TYPE_SENTON, date, null, null, 0, null, null,
+        return new SearchKey(TYPE_SENTON, date, null, 0, null, null,
                 null);
     }
 
     public static SearchKey buildSentSince(DayMonthYear date) {
-        return new SearchKey(TYPE_SENTSINCE, date, null, null, 0, null,
+        return new SearchKey(TYPE_SENTSINCE, date, null, 0, null,
                 null, null);
     }
 
     public static SearchKey buildSince(DayMonthYear date) {
-        return new SearchKey(TYPE_SINCE, date, null, null, 0, null, null,
+        return new SearchKey(TYPE_SINCE, date, null, 0, null, null,
                 null);
     }
 
     // FIELD VALUE
     public static SearchKey buildHeader(String name, String value) {
-        return new SearchKey(TYPE_HEADER, null, null, null, 0, name, value,
+        return new SearchKey(TYPE_HEADER, null, null, 0, name, value,
                 null);
     }
 
     // ONE NUMBER
     public static SearchKey buildLarger(long size) {
-        return new SearchKey(TYPE_LARGER, null, null, null, size, null,
+        return new SearchKey(TYPE_LARGER, null, null, size, null,
                 null, null);
     }
 
     public static SearchKey buildSmaller(long size) {
-        return new SearchKey(TYPE_SMALLER, null, null, null, size, null,
+        return new SearchKey(TYPE_SMALLER, null, null, size, null,
                 null, null);
     }
 
     // NOT
     public static SearchKey buildNot(SearchKey key) {
-        return new SearchKey(TYPE_NOT, null, key, null, 0, null, null, null);
+        final List keys = new ArrayList();
+        keys.add(key);
+        return new SearchKey(TYPE_NOT, null, keys, 0, null, null, null);
     }
 
     // OR
     public static SearchKey buildOr(SearchKey keyOne, SearchKey keyTwo) {
-        return new SearchKey(TYPE_OR, null, keyOne, keyTwo, 0, null, null,
+        final List keys = new ArrayList();
+        keys.add(keyOne);
+        keys.add(keyTwo);
+        return new SearchKey(TYPE_OR, null, keys, 0, null, null,
                 null);
     }
+    
+    /**
+     * Componses an <code>AND</code> key from
+     * given keys.
+     * @param keys <code>List</code> of {@link SearchKey}'s
+     * composing this key
+     * @return <code>SearchKey</code>, not null
+     */
+    public static SearchKey buildAnd(final List keys) {
+        return new SearchKey(TYPE_AND, null, keys, 0, null, null, null);
+    }
 
 
     private final int type;
     private final DayMonthYear date;
-    private final SearchKey one;
-    private final SearchKey two;
+    private final List keys;
     private final long size;
     private final String name;
     private final String value;
     private IdRange[] sequence;
 
-    private SearchKey(final int type, final DayMonthYear date, final SearchKey one, final SearchKey two, 
+    private SearchKey(final int type, final DayMonthYear date, final List keys, 
             final long number, final String name, final String value, IdRange[] sequence) {
         super();
         this.type = type;
         this.date = date;
-        this.one = one;
-        this.two = two;
+        this.keys = keys;
         this.size = number;
         this.name = name;
         this.value = value;
@@ -327,24 +345,15 @@
     public final long getSize() {
         return size;
     }
-
-    /**
-     * Gets key one.
-     * @return the key to be NOT'd when {@link #TYPE_NOT},
-     * the first first to be OR'd when {@link #TYPE_OR},
-     * null otherwise
-     */
-    public final SearchKey getKeyOne() {
-        return one;
-    }
-
+    
     /**
      * Gets key two.
-     * @return the second key to be OR'd when {@link #TYPE_OR},
+     * @return <code>List</code> of <code>SearchKey</code>'s when {@link #TYPE_OR},
+     * {@link #TYPE_AND} or {@link #TYPE_NOT}
      * otherwise null
      */
-    public final SearchKey getKeyTwo() {
-        return two;
+    public final List getKeys() {
+        return keys;
     }
 
     /**
@@ -373,10 +382,9 @@
         int result = 1;
         result = PRIME * result + ((date == null) ? 0 : date.hashCode());
         result = PRIME * result + ((name == null) ? 0 : name.hashCode());
-        result = PRIME * result + ((one == null) ? 0 : one.hashCode());
         result = PRIME * result + Arrays.hashCode(sequence);
         result = PRIME * result + (int) (size ^ (size >>> 32));
-        result = PRIME * result + ((two == null) ? 0 : two.hashCode());
+        result = PRIME * result + ((keys == null) ? 0 : keys.hashCode());
         result = PRIME * result + type;
         result = PRIME * result + ((value == null) ? 0 : value.hashCode());
         return result;
@@ -403,19 +411,14 @@
                 return false;
         } else if (!name.equals(other.name))
             return false;
-        if (one == null) {
-            if (other.one != null)
-                return false;
-        } else if (!one.equals(other.one))
-            return false;
         if (!Arrays.equals(sequence, other.sequence))
             return false;
         if (size != other.size)
             return false;
-        if (two == null) {
-            if (other.two != null)
+        if (keys == null) {
+            if (other.keys != null)
                 return false;
-        } else if (!two.equals(other.two))
+        } else if (!keys.equals(other.keys))
             return false;
         if (type != other.type)
             return false;

Modified: james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParser.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParser.java?rev=632141&r1=632140&r2=632141&view=diff
==============================================================================
--- james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParser.java (original)
+++ james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParser.java Thu Feb 28 14:37:25 2008
@@ -18,6 +18,9 @@
  ****************************************************************/
 package org.apache.james.imapserver.codec.decode.imap4rev1;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.apache.james.api.imap.ImapCommand;
 import org.apache.james.api.imap.ImapMessage;
 import org.apache.james.api.imap.ProtocolException;
@@ -49,6 +52,8 @@
         final char next = request.nextChar();
         if (next >= '0' && next <= '9' || next == '*') {
             return sequenceSet(request);
+        } else if (next == '(') {
+            return paren(request);
         } else {
             final int cap = consumeAndCap(request);
             switch (cap) {
@@ -79,6 +84,24 @@
         }
     }
 
+    private SearchKey paren(ImapRequestLineReader request) throws ProtocolException {
+        request.consume();
+        List keys = new ArrayList();
+        addUntilParen(request, keys);
+        return SearchKey.buildAnd(keys);
+    }
+
+    private void addUntilParen(ImapRequestLineReader request, List keys) throws ProtocolException {
+        final char next = request.nextWordChar();
+        if (next == ')') {
+            request.consume();
+        } else {
+            final SearchKey key = searchKey( request );
+            keys.add(key);
+            addUntilParen(request, keys);
+        }
+    }
+
     private int consumeAndCap(ImapRequestLineReader request) throws ProtocolException {
         final char next = request.consume();
         final int cap = next > 'Z' ? next ^ 32 : next;
@@ -696,11 +719,30 @@
         }
     }
     
-    protected ImapMessage decode(ImapCommand command, ImapRequestLineReader request, String tag, boolean useUids) throws ProtocolException {
-        // Parse the search term from the request
+    public SearchKey decode(ImapRequestLineReader request) throws ProtocolException {
         request.nextWordChar();
-        final SearchKey key = searchKey( request );
+        final SearchKey firstKey = searchKey( request );
+        final SearchKey result;
+        if (request.nextChar() == ' ') {
+            List keys = new ArrayList();
+            keys.add(firstKey);
+            while (request.nextChar() == ' ') {
+                request.nextWordChar();
+                final SearchKey key = searchKey( request );
+                keys.add(key);
+            }
+            result = SearchKey.buildAnd(keys);
+        } else {
+            result = firstKey;
+        }
         endLine( request );
+        return result;
+    }
+    
+    protected ImapMessage decode(ImapCommand command, ImapRequestLineReader request, String tag, boolean useUids) throws ProtocolException {
+        // Parse the search term from the request
+        final SearchKey key = decode( request );
+        
         final ImapMessage result = getMessageFactory().createSearchMessage(command, key, useUids, tag);
         return result;
     }

Added: james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserAndParenthesesTest.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserAndParenthesesTest.java?rev=632141&view=auto
==============================================================================
--- james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserAndParenthesesTest.java (added)
+++ james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserAndParenthesesTest.java Thu Feb 28 14:37:25 2008
@@ -0,0 +1,191 @@
+/****************************************************************
+ * 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.imapserver.codec.decode.imap4rev1;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.james.api.imap.ImapCommand;
+import org.apache.james.api.imap.ImapMessage;
+import org.apache.james.api.imap.ProtocolException;
+import org.apache.james.api.imap.imap4rev1.Imap4Rev1CommandFactory;
+import org.apache.james.api.imap.imap4rev1.Imap4Rev1MessageFactory;
+import org.apache.james.api.imap.message.IdRange;
+import org.apache.james.api.imap.message.request.DayMonthYear;
+import org.apache.james.api.imap.message.request.SearchKey;
+import org.apache.james.imapserver.codec.decode.ImapRequestLineReader;
+import org.apache.james.imapserver.codec.decode.imap4rev1.SearchCommandParserTopLevelAndTest.Input;
+import org.jmock.Mock;
+import org.jmock.MockObjectTestCase;
+
+public class SearchCommandParserAndParenthesesTest extends MockObjectTestCase {
+
+    Input[] variety = {sequence(), uid(), fromHeader(), since(), stringQuoted(), stringUnquoted(), draft(),
+            mailingListHeader(), on(),  unanswered()};
+    
+    public static Input and(Input[] parts, boolean parens) {
+        List keys = new ArrayList();
+        StringBuffer buffer = new StringBuffer();
+        if (parens) {
+            buffer.append("(");            
+        }
+        for (int i = 0; i < parts.length; i++) {
+            if (i>0) {
+                buffer.append(' ');
+            }
+            buffer.append(parts[i].input);
+            keys.add(parts[i].key);
+        }
+        if (parens) {
+            buffer.append(")");
+        }
+        Input input = new Input(buffer.toString(), SearchKey.buildAnd(keys));
+        return input;
+    }
+    
+    public static Input sequence() {
+        IdRange[] range = {new IdRange(Long.MAX_VALUE, 100), new IdRange(110), new IdRange(200, 201),
+                new IdRange(400, Long.MAX_VALUE)};
+        SearchKey key = SearchKey.buildSequenceSet(range);
+        return new Input("*:100,110,200:201,400:*", key);
+    }
+    
+    public static Input uid() {
+        IdRange[] range = {new IdRange(Long.MAX_VALUE, 100), new IdRange(110), new IdRange(200, 201),
+                new IdRange(400, Long.MAX_VALUE)};
+        SearchKey key = SearchKey.buildUidSet(range);
+        return new Input("UID *:100,110,200:201,400:*", key);
+    }
+    
+    public static Input fromHeader() {
+        SearchKey key = SearchKey.buildHeader("FROM", "Smith");
+        return new Input("HEADER FROM Smith", key);
+    }
+    
+    public static Input to() {
+        SearchKey key = SearchKey.buildTo("JAMES Server Development <se...@james.apache.org>");
+        return new Input("To \"JAMES Server Development <se...@james.apache.org>\"", key);
+    }
+    
+    public static Input mailingListHeader() {
+        SearchKey key = SearchKey.buildHeader("Mailing-List", "contact server-dev-help@james.apache.org; run by ezmlm");
+        return new Input("HEADER Mailing-List \"contact server-dev-help@james.apache.org; run by ezmlm\"", key);
+    }
+    
+    public static Input since() {
+        SearchKey key = SearchKey.buildSince(new DayMonthYear(11,1, 2001));
+        return new Input("since 11-Jan-2001", key);
+    }
+    
+    public static Input on() {
+        SearchKey key = SearchKey.buildOn(new DayMonthYear(1,2, 2001));
+        return new Input("on 1-Feb-2001", key);
+    }
+    
+    public static Input stringUnquoted() {
+        SearchKey key = SearchKey.buildFrom("Smith");
+        return new Input("FROM Smith", key);
+    }
+    
+    public static Input stringQuoted() {
+        SearchKey key = SearchKey.buildFrom("Smith And Jones");
+        return new Input("FROM \"Smith And Jones\"", key);
+    }
+    
+    public static Input draft() {
+        SearchKey key = SearchKey.buildDraft();
+        return new Input("DRAFT", key);
+    }
+    
+    public static Input unanswered() {
+        SearchKey key = SearchKey.buildUnanswered();
+        return new Input("unanswered", key);
+    }
+    
+    public static final class Input {
+        public String input;
+        public SearchKey key;
+        
+        public Input(String input, SearchKey key) {
+            super();
+            this.input = input;
+            this.key = key;
+        }
+    }
+    
+    SearchCommandParser parser;
+    Mock mockCommandFactory;
+    Mock mockMessageFactory;
+    Mock mockCommand;
+    Mock mockMessage;
+    ImapCommand command;
+    ImapMessage message;
+    
+    protected void setUp() throws Exception {
+        super.setUp();
+        parser = new SearchCommandParser();
+        mockCommandFactory = mock(Imap4Rev1CommandFactory.class);
+        mockCommandFactory.expects(once()).method("getSearch");
+        mockMessageFactory = mock(Imap4Rev1MessageFactory.class);
+        mockCommand = mock(ImapCommand.class);
+        command = (ImapCommand) mockCommand.proxy();
+        mockMessage = mock(ImapMessage.class);
+        message = (ImapMessage) mockMessage.proxy();
+        parser.init((Imap4Rev1CommandFactory) mockCommandFactory.proxy());
+        parser.setMessageFactory((Imap4Rev1MessageFactory) mockMessageFactory.proxy());
+    }
+
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testShouldParseTopLevelParentheses() throws Exception {
+        check(and(variety, true));
+    }
+    
+    public void testShouldParseDeepParentheses() throws Exception {
+        Input[] deep = {and(variety, true), and(variety, true), sequence(), and(variety, true), draft(), mailingListHeader()};
+        Input[] mid = {and(deep, true), since(), and(variety, true), unanswered()};
+        Input[] top = {uid(), and(deep, true), and(mid, true), stringQuoted(), and(mid, true)};
+        check(and(top, true));
+    }
+    
+    public void testShouldParseParenthesesOnTopLevel() throws Exception {
+        Input[] deep = {and(variety, true), and(variety, true), sequence(), and(variety, true), draft(), mailingListHeader()};
+        Input[] mid = {and(deep, true), since(), and(variety, true), unanswered()};
+        Input[] top = {uid(), and(deep, true), and(mid, true), stringQuoted(), and(mid, true)};
+        check(and(top, false));
+    }
+    
+    private void check(Input in) throws UnsupportedEncodingException, ProtocolException {
+        String input = in.input + "\r\n";
+        ImapRequestLineReader reader = new ImapRequestLineReader(
+                new ByteArrayInputStream(input.getBytes("US-ASCII")), 
+                    new ByteArrayOutputStream());
+
+        final SearchKey result = parser.decode(reader);
+        assertEquals(in.key, result);
+    }
+
+
+}

Modified: james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserOrTest.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserOrTest.java?rev=632141&r1=632140&r2=632141&view=diff
==============================================================================
--- james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserOrTest.java (original)
+++ james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserOrTest.java Thu Feb 28 14:37:25 2008
@@ -34,7 +34,6 @@
 import org.jmock.MockObjectTestCase;
 
 public class SearchCommandParserOrTest extends MockObjectTestCase {
-    
     SearchCommandParser parser;
     Mock mockCommandFactory;
     Mock mockMessageFactory;

Modified: james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserSearchKeySequenceSetTest.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserSearchKeySequenceSetTest.java?rev=632141&r1=632140&r2=632141&view=diff
==============================================================================
--- james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserSearchKeySequenceSetTest.java (original)
+++ james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserSearchKeySequenceSetTest.java Thu Feb 28 14:37:25 2008
@@ -27,7 +27,6 @@
 import org.apache.james.api.imap.imap4rev1.Imap4Rev1CommandFactory;
 import org.apache.james.api.imap.imap4rev1.Imap4Rev1MessageFactory;
 import org.apache.james.api.imap.message.IdRange;
-import org.apache.james.api.imap.message.request.DayMonthYear;
 import org.apache.james.api.imap.message.request.SearchKey;
 import org.apache.james.imapserver.codec.decode.ImapRequestLineReader;
 import org.jmock.Mock;
@@ -35,7 +34,6 @@
 
 public class SearchCommandParserSearchKeySequenceSetTest extends MockObjectTestCase {
 
-    private static final DayMonthYear DATE = new DayMonthYear(1, 1, 2000);
     SearchCommandParser parser;
     Mock mockCommandFactory;
     Mock mockMessageFactory;

Added: james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserTopLevelAndTest.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserTopLevelAndTest.java?rev=632141&view=auto
==============================================================================
--- james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserTopLevelAndTest.java (added)
+++ james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/decode/imap4rev1/SearchCommandParserTopLevelAndTest.java Thu Feb 28 14:37:25 2008
@@ -0,0 +1,193 @@
+/****************************************************************
+ * 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.imapserver.codec.decode.imap4rev1;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.james.api.imap.ImapCommand;
+import org.apache.james.api.imap.ImapMessage;
+import org.apache.james.api.imap.ProtocolException;
+import org.apache.james.api.imap.imap4rev1.Imap4Rev1CommandFactory;
+import org.apache.james.api.imap.imap4rev1.Imap4Rev1MessageFactory;
+import org.apache.james.api.imap.message.IdRange;
+import org.apache.james.api.imap.message.request.DayMonthYear;
+import org.apache.james.api.imap.message.request.SearchKey;
+import org.apache.james.imapserver.codec.decode.ImapRequestLineReader;
+import org.jmock.Mock;
+import org.jmock.MockObjectTestCase;
+
+public class SearchCommandParserTopLevelAndTest extends MockObjectTestCase {
+
+    Input[] one = {sequence()};
+    
+    Input[] base = {sequence(), uid(), fromHeader(), since(), stringQuoted(), stringUnquoted(), draft()};
+    
+    Input[] variety = {sequence(), uid(), fromHeader(), since(), stringQuoted(), stringUnquoted(), draft(),
+            mailingListHeader(), on(),  unanswered(), };
+    
+    public static Input sequence() {
+        IdRange[] range = {new IdRange(Long.MAX_VALUE, 100), new IdRange(110), new IdRange(200, 201),
+                new IdRange(400, Long.MAX_VALUE)};
+        SearchKey key = SearchKey.buildSequenceSet(range);
+        return new Input("*:100,110,200:201,400:*", key);
+    }
+    
+    public static Input uid() {
+        IdRange[] range = {new IdRange(Long.MAX_VALUE, 100), new IdRange(110), new IdRange(200, 201),
+                new IdRange(400, Long.MAX_VALUE)};
+        SearchKey key = SearchKey.buildUidSet(range);
+        return new Input("UID *:100,110,200:201,400:*", key);
+    }
+    
+    public static Input fromHeader() {
+        SearchKey key = SearchKey.buildHeader("FROM", "Smith");
+        return new Input("HEADER FROM Smith", key);
+    }
+    
+    public static Input to() {
+        SearchKey key = SearchKey.buildTo("JAMES Server Development <se...@james.apache.org>");
+        return new Input("To \"JAMES Server Development <se...@james.apache.org>\"", key);
+    }
+    
+    public static Input mailingListHeader() {
+        SearchKey key = SearchKey.buildHeader("Mailing-List", "contact server-dev-help@james.apache.org; run by ezmlm");
+        return new Input("HEADER Mailing-List \"contact server-dev-help@james.apache.org; run by ezmlm\"", key);
+    }
+    
+    public static Input since() {
+        SearchKey key = SearchKey.buildSince(new DayMonthYear(11,1, 2001));
+        return new Input("since 11-Jan-2001", key);
+    }
+    
+    public static Input on() {
+        SearchKey key = SearchKey.buildOn(new DayMonthYear(1,2, 2001));
+        return new Input("on 1-Feb-2001", key);
+    }
+    
+    public static Input stringUnquoted() {
+        SearchKey key = SearchKey.buildFrom("Smith");
+        return new Input("FROM Smith", key);
+    }
+    
+    public static Input stringQuoted() {
+        SearchKey key = SearchKey.buildFrom("Smith And Jones");
+        return new Input("FROM \"Smith And Jones\"", key);
+    }
+    
+    public static Input draft() {
+        SearchKey key = SearchKey.buildDraft();
+        return new Input("DRAFT", key);
+    }
+    
+    public static Input unanswered() {
+        SearchKey key = SearchKey.buildUnanswered();
+        return new Input("unanswered", key);
+    }
+    
+    public static final class Input {
+        public String input;
+        public SearchKey key;
+        
+        public Input(String input, SearchKey key) {
+            super();
+            this.input = input;
+            this.key = key;
+        }
+    }
+    
+    SearchCommandParser parser;
+    Mock mockCommandFactory;
+    Mock mockMessageFactory;
+    Mock mockCommand;
+    Mock mockMessage;
+    ImapCommand command;
+    ImapMessage message;
+    
+    protected void setUp() throws Exception {
+        super.setUp();
+        parser = new SearchCommandParser();
+        mockCommandFactory = mock(Imap4Rev1CommandFactory.class);
+        mockCommandFactory.expects(once()).method("getSearch");
+        mockMessageFactory = mock(Imap4Rev1MessageFactory.class);
+        mockCommand = mock(ImapCommand.class);
+        command = (ImapCommand) mockCommand.proxy();
+        mockMessage = mock(ImapMessage.class);
+        message = (ImapMessage) mockMessage.proxy();
+        parser.init((Imap4Rev1CommandFactory) mockCommandFactory.proxy());
+        parser.setMessageFactory((Imap4Rev1MessageFactory) mockMessageFactory.proxy());
+    }
+
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+    
+    public void testLargePermutations() throws Exception {
+        permute(16, one);
+        permute(32, one);
+    }
+    
+    public void testBasePermutations() throws Exception {
+        permute(2, base);
+        permute(3, base);
+        permute(4, base);
+        permute(5, base);
+    }
+    
+    public void testVarietyPermutations() throws Exception {
+        permute(5, variety);
+    }
+    
+    private void permute(int mutations, Input[] inputs) throws Exception {
+        permute(mutations, new ArrayList(), new StringBuffer(), inputs);
+    }
+    
+    private void permute(int mutations, List keys, StringBuffer buffer, Input[] inputs) throws Exception {
+        if (mutations == 0) {
+            check(keys, buffer);
+        } else {
+            mutations -= 1;
+            for (int i = 0; i < inputs.length; i++) {
+                StringBuffer nextBuffer = new StringBuffer(buffer);
+                if (nextBuffer.length() > 0) {
+                    nextBuffer.append(' ');
+                }
+                nextBuffer.append(inputs[i].input);
+                List nextKeys = new ArrayList(keys);
+                nextKeys.add(inputs[i].key);
+                permute(mutations, nextKeys, nextBuffer, inputs);
+            }
+        }
+    }
+
+    private void check(List keys, StringBuffer buffer) throws UnsupportedEncodingException, ProtocolException {
+        buffer.append("\r\n");
+        String input = buffer.toString();
+        SearchKey key = SearchKey.buildAnd(keys);
+        ImapRequestLineReader reader = new ImapRequestLineReader(
+                new ByteArrayInputStream(input.getBytes("US-ASCII")), 
+                    new ByteArrayOutputStream());
+
+        assertEquals(input, key, parser.decode(reader));
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org