made Headers class truly immutable
[org.ibex.mail.git] / src / org / ibex / mail / Headers.java
index 26c12ad..a68d9d7 100644 (file)
@@ -15,22 +15,9 @@ import java.net.*;
 import java.io.*;
 
 // FIXME: this is important: folded headers: can insert CRLF anywhere that whitespace appears (before the whitespace)
-public class Headers extends JS.Immutable implements Fountain {
-    private final Hash head = new Hash();
-    private final Hash headModified = new Hash();
-    public        int lines;
-    public  final boolean mime;
+public abstract class Headers extends JS.Immutable implements Fountain {
 
-    private String raw;
-    private StringFountain fountain;
-
-    public String get(String s) {
-        String ret = (String)headModified.get(s.toLowerCase());
-        if (ret==null) ret = (String)head.get(s.toLowerCase());
-        return ret;
-    }
-    public void remove(String k) { put(k, null); }
-    public void put(String k, String v) {
+    public Headers set(String k, String v) {
         Stream stream = getStream();
         StringBuffer all = new StringBuffer();
         int lines = 0;
@@ -53,59 +40,85 @@ public class Headers extends JS.Immutable implements Fountain {
             all.append(k + ": " + v + "\r\n");
         }
         all.append("\r\n");
-        this.raw = all.toString();
-        this.lines = lines;
-        this.fountain = new Fountain.StringFountain(this.raw);
+        return new Original(new Stream(all.toString()));
     }
-    public JS get(JS s) throws JSExn { return JSU.S(get(JSU.toString(s).toLowerCase())); }
-
-    public Stream getStream() { return fountain.getStream(); }
-    public int    getLength() { return fountain.getLength(); }
-    public int    getNumLines() { return fountain.getNumLines(); }
-    public Stream getStreamWithCRLF() { return new Stream(raw+"\r\n"); }
     
     // FIXME
-    public String getString() { return raw; }
+    public abstract String getString();
 
-    public Headers(Stream stream) throws Malformed { this(stream, false); }
-    public Headers(Stream stream, boolean assumeMime) throws Malformed {
-        StringBuffer all = new StringBuffer();
-        String key = null;
-        int lines = 0;
-        for(String s = stream.readln(); s != null && !s.equals(""); s = stream.readln()) {
-            all.append(s);
-            all.append("\r\n");
-            lines++;
-            if (Character.isSpace(s.charAt(0))) {
-                if (key == null) throw new Malformed("Message began with a blank line; no headers");
-                head.put(key, head.get(key) + " " + s.trim());
-                continue;
+    public abstract String get(String s);
+    public abstract java.util.Enumeration names();
+
+    public Headers set(String[] keyval) {
+        Headers ret = this;
+        for(int i=0; i<keyval.length; i+=2)
+            ret = ret.set(keyval[i], keyval[i+1]);
+        return ret;
+    }
+    public Headers remove(String key) { return set(key, null); /* FIXME */ }
+
+    public String getLowerCaseTrimmed(String s) {
+        String ret = get(s);
+        return ret==null ? null : ret.toLowerCase();
+    }
+    
+    public static class Original extends Headers {
+        private final Hash head = new Hash();
+        private final Hash headModified = new Hash();
+        public        int lines;
+        public  final boolean mime;
+        private String raw;
+        private StringFountain fountain;
+
+        public String getString() { return raw; }
+        public Stream getStream() { return fountain.getStream(); }
+        public int    getLength() { return fountain.getLength(); }
+        public int    getNumLines() { return fountain.getNumLines(); }
+        public Stream getStreamWithCRLF() { return new Stream(raw+"\r\n"); }
+        public JS get(JS s) throws JSExn { return JSU.S(get(JSU.toString(s).toLowerCase())); }
+        public java.util.Enumeration names() { return head.keys(); }
+        
+        public String get(String s) { return (String)head.get(s.toLowerCase()); }
+        public Original(Stream stream) throws Malformed { this(stream, false); }
+        public Original(Stream stream, boolean assumeMime) throws Malformed {
+            StringBuffer all = new StringBuffer();
+            String key = null;
+            int lines = 0;
+            for(String s = stream.readln(); s != null && !s.equals(""); s = stream.readln()) {
+                all.append(s);
+                all.append("\r\n");
+                lines++;
+                if (Character.isSpace(s.charAt(0))) {
+                    if (key == null) throw new Malformed("Message began with a blank line; no headers");
+                    head.put(key, head.get(key) + " " + s.trim());
+                    continue;
+                }
+                if (s.indexOf(':') == -1) throw new Malformed("Header line does not contain colon: " + s);
+                key = s.substring(0, s.indexOf(':')).toLowerCase();
+                for(int i=0; i<key.length(); i++)
+                    if (key.charAt(i) < 33 || key.charAt(i) > 126)
+                        throw new Malformed("Header key \""+key+"\" contains invalid character \"" + key.charAt(i) + "\"");
+                String val = s.substring(s.indexOf(':') + 1).trim();
+                if (get(key) != null) val = get(key) + " " + val; // just append it to the previous one;
+                head.put(key, val);
             }
-            if (s.indexOf(':') == -1) throw new Malformed("Header line does not contain colon: " + s);
-            key = s.substring(0, s.indexOf(':')).toLowerCase();
-            for(int i=0; i<key.length(); i++)
-                if (key.charAt(i) < 33 || key.charAt(i) > 126)
-                    throw new Malformed("Header key \""+key+"\" contains invalid character \"" + key.charAt(i) + "\"");
-            String val = s.substring(s.indexOf(':') + 1).trim();
-            if (get(key) != null) val = get(key) + " " + val; // just append it to the previous one;
-            head.put(key, val);
+            this.raw = all.toString();
+            this.fountain = new Fountain.StringFountain(this.raw);
+            this.lines = lines;
+            this.mime = assumeMime | (get("mime-version") != null && get("mime-version").trim().equals("1.0"));
+            /*
+              java.util.Enumeration e = head.keys();
+              while(e.hasNext()) {
+              String k = (String)e.next();
+              String v = (String)head.get(k);
+              if (mime) k = Encode.RFC2047.decode(k);
+              v = uncomment(v);
+              if (mime) v = Encode.RFC2047.decode(v);
+              head.put(k, v);
+              }
+            */
         }
-        this.raw = all.toString();
-        this.fountain = new Fountain.StringFountain(this.raw);
-        this.lines = lines;
-        this.mime = assumeMime | (get("mime-version") != null && get("mime-version").trim().equals("1.0"));
-        /*
-          java.util.Enumeration e = head.keys();
-          while(e.hasNext()) {
-          String k = (String)e.next();
-          String v = (String)head.get(k);
-          if (mime) k = Encode.RFC2047.decode(k);
-          v = uncomment(v);
-          if (mime) v = Encode.RFC2047.decode(v);
-          head.put(k, v);
-          }
-        */
-    }
+
 
     // Helpers //////////////////////////////////////////////////////////////////////////////
 
@@ -123,4 +136,5 @@ public class Headers extends JS.Immutable implements Fountain {
         }
         return val;
     }
+    }
 }