formatting
[org.ibex.mail.git] / src / org / ibex / mail / Query.java
index bc8495c..a260dee 100644 (file)
@@ -1,5 +1,11 @@
+// Copyright 2000-2005 the Contributors, as shown in the revision logs.
+// Licensed under the Apache Public Source License 2.0 ("the License").
+// You may not use this file except in compliance with the License.
+
 package org.ibex.mail;
 import java.util.*;
+import org.ibex.mail.target.*;
+import org.ibex.util.*;
 
 /**
  *  [immutable] This class encapsulates a query against a mailbox.
@@ -14,38 +20,54 @@ import java.util.*;
  */
 public class Query {
 
-    public static Query not(Query q)            { return new Query(NOT, new Query[] { q },      0, 0, 0, null, null, null, null); }
-    public static Query and(Query q1, Query q2) { return new Query(AND, new Query[] { q1, q2 }, 0, 0, 0, null, null, null, null); }
-    public static Query and(Query[] q)          { return new Query(AND, q,                      0, 0, 0, null, null, null, null); }
-    public static Query or(Query q1, Query q2)  { return new Query(OR,  new Query[] { q1, q2 }, 0, 0, 0, null, null, null, null); }
-    public static Query or(Query[] q)           { return new Query(OR,  q,                      0, 0, 0, null, null, null, null); }
-    public static Query uid(int min, int max)   { return new Query(UID, null, min, max, 0, null, null, null, null); }
-    public static Query messagenum(int min, int max) { return new Query(MESSAGENUM, null, min, max, 0, null, null, null, null); }
-    public static Query sent(Date earliest, Date latest)    { return new Query(SENT, null,0,0,0,null,null, earliest, latest); }
-    public static Query arrival(Date earliest, Date latest) { return new Query(ARRIVAL, null,0,0,0,null,null, earliest, latest); }
-    public static Query header(String name, String val)     { return new Query(HEADER, null, 0, 0, 0, name, val, null, null); }
-    public static Query size(int min, int max)              { return new Query(SIZE, null, min, max, 0, null, null, null, null); }
-    public static Query flags(int flags)                    { return new Query(FLAGS, null, 0, 0, flags, null, null, null, null); }
-    public static Query body(String text)                   { return new Query(BODY, null, 0, 0, 0, null, text, null, null); }
-    public static Query body(String text)                   { return new Query(FULL, null, 0, 0, 0, null, text, null, null); }
+    public static Query not(Query q)                { return new Query(NOT, new Query[] { q },0,0,0,null,null,null,null,null); }
+    public static Query and(Query q1, Query q2)     { return new Query(AND,new Query[]{q1,q2},0,0,0,null,null,null,null,null); }
+    public static Query and(Query[] q)              { return new Query(AND, q,0,0,0,null, null, null, null, null ); }
+    public static Query or(Query q1, Query q2)      { return new Query(OR,new Query[] {q1,q2},0,0,0,null,null, null, null, null); }
+    public static Query or(Query[] q)               { return new Query(OR, q, 0, 0, 0, null, null, null, null, null); }
+    public static Query uid(int min, int max)       { return new Query(UID, null, min, max, 0, null, null, null, null, null); }
+    public static Query imapNumber(int min,int max) { return new Query(IMAPNUM,null,min,max,0,null,null,null,null, null); }
+    public static Query nntpNumber(int min,int max) { return new Query(NNTPNUM,null,min,max,0,null,null,null,null, null); }
+    public static Query sent(Date e, Date l)        { return new Query(SENT,null,0,0,0,null,null,e,l,null); }
+    public static Query arrival(Date e, Date l)     { return new Query(ARRIVAL,null,0,0,0,null,null, e,l,null);}
+    public static Query header(String k, String v)  { return new Query(HEADER, null, 0, 0, 0, k, v, null, null, null);}
+    public static Query size(int min, int max)      { return new Query(SIZE, null, min, max, 0, null, null, null, null, null);}
+    public static Query body(String text)           { return new Query(BODY, null, 0, 0, 0, null, text, null, null, null);}
+    public static Query full(String text)           { return new Query(FULL, null, 0, 0, 0, null, text, null, null, null);}
+    public static Query uid(int[] set)              { return new Query(UID,  null, 0 ,0 ,0, null, null, null, null, set);}
+    public static Query imapNumber(int[] set)       { return new Query(IMAPNUM,  null, 0 ,0 ,0, null, null, null, null, set);}
+    public static Query all()                       { return new Query(ALL, null, 0, 0, 0, null, null, null, null, null); }
+    public static Query deleted()                   { return new Query(DELETED, null, 0, 0, 0, null, null, null, null, null); }
+    public static Query seen()                      { return new Query(SEEN, null, 0, 0, 0, null, null, null, null, null); }
+    public static Query flagged()                   { return new Query(FLAGGED, null, 0, 0, 0, null, null, null, null, null); }
+    public static Query draft()                     { return new Query(DRAFT, null, 0, 0, 0, null, null, null, null, null); }
+    public static Query answered()                  { return new Query(ANSWERED, null, 0, 0, 0, null, null, null, null, null); }
+    public static Query recent()                    { return new Query(RECENT, null, 0, 0, 0, null, null, null, null, null); }
+    //public static Query set(boolean uid, int[] set) { return uid ? uid(set) : imapNumber(set); }
 
-    private Query(int type, Query[] q, int min, int max, int flags, String key, String text, Date earliest, Date latest) {
+    private Query(int type, Query[] q,int min,int max, int flags, String key, String text, Date earliest, Date latest, int[] set) {
         this.type = type; this.q = q; this.min = min; this.max = max; this.flags = flags; this.key = key; this.text = text;
-        this.earliest = earliest; this.latest = latest; }
+        this.earliest = earliest; this.latest = latest; this.set = set; }
 
-    public static int ALL        = 0;
-    public static int NOT        = 1;
-    public static int AND        = 2;
-    public static int OR         = 3;
-    public static int UID        = 4;
-    public static int MESSAGENUM = 5; 
-    public static int SENT       = 6;
-    public static int ARRIVAL    = 7;
-    public static int HEADER     = 8;
-    public static int SIZE       = 9;
-    public static int FLAGS      = 10;
-    public static int BODY       = 11;
-    public static int FULL       = 12;
+    public static final int ALL        = 0;
+    public static final int NOT        = 1;
+    public static final int AND        = 2;
+    public static final int OR         = 3;
+    public static final int UID        = 4;
+    public static final int SENT       = 5;
+    public static final int ARRIVAL    = 6;
+    public static final int HEADER     = 7;
+    public static final int SIZE       = 8;
+    public static final int BODY       = 9;
+    public static final int FULL       = 10;
+    public static final int IMAPNUM    = 11;
+    public static final int DELETED    = 12;
+    public static final int SEEN       = 13;
+    public static final int FLAGGED    = 14;
+    public static final int DRAFT      = 15;
+    public static final int ANSWERED   = 16;
+    public static final int RECENT     = 17;
+    public static final int NNTPNUM    = 18;
 
     public final int type;
     public final Query[] q;
@@ -56,24 +78,99 @@ public class Query {
     public final String text;
     public final Date earliest;
     public final Date latest;
+    public final int[] set;
 
-    public boolean match(Mailbox mbox, Message m) {
+    public boolean match(Mailbox.Iterator it) {
         switch(type) {
             case ALL:        return true;
-            case NOT:        return !q[0].match(mbox, m);
-            case OR:         for(int i=0; i<q.length; i++) if (q[i].match(mbox, m)) return true; return false;
-            case AND:        for(int i=0; i<q.length; i++) if (!q[i].match(mbox, m)) return false; return true;
-            case UID:        return mbox.uid(m)        >= min && mbox.uid <= max;
-            case MESSAGENUM: return mbox.messagenum(m) >= min && mbox.messagenum(m) <= max;
-            case SENT:       return (latest == null || m.sent.before(latest))    &&(earliest == null || m.sent.after(earliest));
-            case ARRIVAL:    return (latest == null || m.arrival.before(latest)) &&(earliest == null || m.arrival.after(earliest));
-            case SIZE:       return m.size >= min && m.size <= max;
-            case FLAGS:      return mbox.getFlags(flags);
-            case HEADER:     return m.headers.get(key) != null && ((String)m.headers.get(key)).indexOf(text) != -1;
-            case BODY:       return m.body.indexOf(text) != -1;
-            case FULL:       return m.body.indexOf(text) != -1 || m.allHeaders.indexOf(text) != -1;
+            case NOT:        return !q[0].match(it);
+            case OR:         for(int i=0; i<q.length; i++) if (q[i].match(it)) return true; return false;
+            case AND:        for(int i=0; i<q.length; i++) if (!q[i].match(it)) return false; return true;
+            case UID:        if (set != null) {
+                                 for(int i=0; i<set.length; i+=2)
+                                    if (set[i] <= it.uid() && set[i+1] >= it.uid()) return true;
+                                 return false; }
+                             else return it.uid() >= min && it.uid() <= max;
+            case IMAPNUM:    if (set != null) {
+                                 for(int i=0; i<set.length; i+=2) if (set[i] <= it.imapNumber() && set[i+1] >= it.imapNumber()) return true;
+                                 return false; }
+                             else return it.imapNumber() >= min && it.imapNumber() <= max;
+            case NNTPNUM:    if (set != null) {
+                                 for(int i=0; i<set.length; i+=2) if (set[i] <= it.nntpNumber() && set[i+1] >= it.nntpNumber()) return true;
+                                 return false; }
+                             else return it.imapNumber() >= min && it.imapNumber() <= max;
+            case SENT:       return (latest==null||it.cur().date.before(latest)) && 
+                                    (earliest==null||it.cur().date.after(earliest));
+            case ARRIVAL:    return (latest == null || it.cur().arrival.before(latest)) &&
+                                    (earliest == null || it.cur().arrival.after(earliest));
+            case HEADER:     return it.cur().headers.get(key) != null &&
+                                 ((String)it.cur().headers.get(key)).toLowerCase().indexOf(text.toLowerCase()) != -1;
+            case DELETED:    return (it.getFlags() & Mailbox.Flag.DELETED) !=0;
+            case SEEN:       return (it.getFlags() & Mailbox.Flag.SEEN)!=0;
+            case FLAGGED:    return (it.getFlags() & Mailbox.Flag.FLAGGED)!=0;
+            case DRAFT:      return (it.getFlags() & Mailbox.Flag.DRAFT)!=0;
+            case ANSWERED:   return (it.getFlags() & Mailbox.Flag.ANSWERED)!=0;
+            case RECENT:     return (it.getFlags() & Mailbox.Flag.RECENT)!=0;
+
+            // FIXME: super inefficient
+            case BODY:       throw new RuntimeException("BODY searches are not supported because they are slow");
+            case FULL:       throw new RuntimeException("FULL searches are not supported because they are slow");
+            case SIZE:       throw new RuntimeException("SIZE searches are not supported because Adam is lame");
+                //return it.cur().size() >= min && it.cur().size() <= max;
+
             default:         throw new Error("this should not happen");
         }
     }
 
+
+    // FIXME: normalize multiple-range queries using AND/OR?
+
+    private static String range(int[] set, int min, int max, String name) {
+        if (set != null && set.length==1) { min = set[0]; max = set[1]; set = null; }
+        if (set == null) {
+            if (min==max) return "name=="+min;
+            return min+"<="+name+"<="+max;
+        } else {
+            StringBuffer sb = new StringBuffer();
+            for(int i=0; i<set.length; i+=2) {
+                if (i>0) sb.append(" || ");
+                sb.append(set[i]+"<="+name+"<="+set[i+1]);
+            }
+            return sb.toString();
+        }
+    }
+
+    private static String range(Date earliest, Date latest, String name) {
+        StringBuffer sb = new StringBuffer();
+        if (earliest != null) sb.append(earliest + "<=");
+        sb.append(name);
+        if (latest != null) sb.append("<="+latest);
+        return sb.toString();
+    }
+
+    public String toString() {
+        switch(type) {
+            case ALL:        return "ALL";
+            case NOT:        return "NOT " + q[0];
+            case OR:         return StringUtil.join(q, " || ");
+            case AND:        return StringUtil.join(q, " && ");
+            case UID:        return range(set, min, max, "uid");
+            case IMAPNUM:    return range(set, min, max, "imapnum");
+            case NNTPNUM:    return range(set, min, max, "nntpnum");
+            case SENT:       return range(earliest, latest, "sent");
+            case ARRIVAL:    return range(earliest, latest, "arrival");
+            case HEADER:     return "headers["+key+"]=="+text;
+            case DELETED:    return "deleted";
+            case SEEN:       return "seen";
+            case FLAGGED:    return "flagged";
+            case DRAFT:      return "draft";
+            case ANSWERED:   return "answered";
+            case RECENT:     return "recent";
+            // FIXME: super inefficient
+            case BODY:       return "<bodysearch>";
+            case FULL:       return "<fullsearch>";
+            case SIZE:       return "<sizesearch>";
+            default:         throw new Error("this should not happen");
+        }
+    }
 }