You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by jb...@apache.org on 2005/01/20 06:08:03 UTC

svn commit: r125713 - /geronimo/trunk/specs/javamail/src/java/javax/mail/Flags.java

Author: jboynes
Date: Wed Jan 19 21:07:59 2005
New Revision: 125713

URL: http://svn.apache.org/viewcvs?view=rev&rev=125713
Log:
The serialized form of this class means that system flags need to be implemented as a bitmap.

Also, user flags need to serialized as a Hashtable, so it seems simpler to store them that way.


Modified:
   geronimo/trunk/specs/javamail/src/java/javax/mail/Flags.java

Modified: geronimo/trunk/specs/javamail/src/java/javax/mail/Flags.java
Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/Flags.java?view=diff&rev=125713&p1=geronimo/trunk/specs/javamail/src/java/javax/mail/Flags.java&r1=125712&p2=geronimo/trunk/specs/javamail/src/java/javax/mail/Flags.java&r2=125713
==============================================================================
--- geronimo/trunk/specs/javamail/src/java/javax/mail/Flags.java	(original)
+++ geronimo/trunk/specs/javamail/src/java/javax/mail/Flags.java	Wed Jan 19 21:07:59 2005
@@ -18,159 +18,244 @@
 package javax.mail;
 
 import java.io.Serializable;
-import java.util.HashMap;
+import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 
 /**
+ * Representation of flags that may be associated with a message.
+ * Flags can either be system flags, defined by the {@link Flags.Flag Flag} inner class,
+ * or user-defined flags defined by a String. The system flags represent those expected
+ * to be provided by most folder systems; user-defined flags allow for additional flags
+ * on a per-provider basis.
+ * <p/>
+ * This class is Serializable but compatibility is not guaranteed across releases.
+ *
  * @version $Rev$ $Date$
  */
 public class Flags implements Cloneable, Serializable {
     public static final class Flag {
-        public static final Flag ANSWERED = new Flag("Answered", true);
-        public static final Flag DELETED = new Flag("Deleted", true);
-        public static final Flag DRAFT = new Flag("Draft", true);
-        public static final Flag FLAGGED = new Flag("Flagged", true);
-        public static final Flag RECENT = new Flag("Recent", true);
-        public static final Flag SEEN = new Flag("Seen", true);
-        public static final Flag USER = new Flag("", false);
-        private String _name;
-        private boolean _system;
-
-        private Flag(String name) {
-            this(name, false);
-        }
-
-        private Flag(String name, boolean system) {
-            if (name == null) {
-                throw new IllegalArgumentException("Flag name cannot be null");
-            }
-            _name = name;
-            _system = system;
-        }
-
-        private String getName() {
-            return _name;
-        }
-
-        private boolean isSystemFlag() {
-            return _system;
+        /**
+         * Flag that indicates that the message has been replied to; has a bit value of 1.
+         */
+        public static final Flag ANSWERED = new Flag(1);
+        /**
+         * Flag that indicates that the message has been marked for deletion and
+         * should be removed on a subsequent expunge operation; has a bit value of 2.
+         */
+        public static final Flag DELETED = new Flag(2);
+        /**
+         * Flag that indicates that the message is a draft; has a bit value of 4.
+         */
+        public static final Flag DRAFT = new Flag(4);
+        /**
+         * Flag that indicates that the message has been flagged; has a bit value of 8.
+         */
+        public static final Flag FLAGGED = new Flag(8);
+        /**
+         * Flag that indicates that the message has been delivered since the last time
+         * this folder was opened; has a bit value of 16.
+         */
+        public static final Flag RECENT = new Flag(16);
+        /**
+         * Flag that indicates that the message has been viewed; has a bit value of 32.
+         * This flag is set by the {@link Message#getInputStream()} and {@link Message#getContent()}
+         * methods.
+         */
+        public static final Flag SEEN = new Flag(32);
+        /**
+         * Flags that indicates if this folder supports user-defined flags; has a bit value of 0x80000000.
+         */
+        public static final Flag USER = new Flag(0x80000000);
+
+        private final int mask;
+
+        private Flag(int mask) {
+            this.mask = mask;
         }
     }
 
-    private static final Flag[] FLAG_ARRAY = new Flag[0];
-    private static final String[] STRING_ARRAY = new String[0];
-    private Map _map = new HashMap(4);
-
+    // the Serialized form of this class required the following two fields to be persisted
+    // this leads to a specific type of implementation
+    private int system_flags;
+    private final Hashtable user_flags;
+
+    /**
+     * Construct a Flags instance with no flags set.
+     */
     public Flags() {
+        user_flags = new Hashtable();
     }
 
+    /**
+     * Construct a Flags instance with a supplied system flag set.
+     * @param flag the system flag to set
+     */
     public Flags(Flag flag) {
-        add(flag);
+        system_flags = flag.mask;
+        user_flags = new Hashtable();
     }
 
+    /**
+     * Construct a Flags instance with a same flags set.
+     * @param flags the instance to copy
+     */
     public Flags(Flags flags) {
-        add(flags);
+        system_flags = flags.system_flags;
+        user_flags = new Hashtable(flags.user_flags);
     }
 
+    /**
+     * Construct a Flags instance with the supplied user flags set.
+     * Question: should this automatically set the USER system flag?
+     * @param name the user flag to set
+     */
     public Flags(String name) {
-        add(name);
+        user_flags = new Hashtable();
+        user_flags.put(name.toLowerCase(), name);
     }
 
+    /**
+     * Set a system flag.
+     * @param flag the system flag to set
+     */
     public void add(Flag flag) {
-        _map.put(flag.getName(), flag);
+        system_flags |= flag.mask;
     }
 
+    /**
+     * Set all system and user flags from the supplied Flags.
+     * Question: do we need to check compatibility of USER flags?
+     * @param flags the Flags to add
+     */
     public void add(Flags flags) {
-        _map.putAll(flags._map);
+        system_flags |= flags.system_flags;
+        user_flags.putAll(flags.user_flags);
     }
 
+    /**
+     * Set a user flag.
+     * Question: should this fail if the USER system flag is not set?
+     * @param name the user flag to set
+     */
     public void add(String name) {
-        add(new Flag(name));
+        user_flags.put(name.toLowerCase(), name);
     }
 
+    /**
+     * Return a copy of this instance.
+     * @return a copy of this instance
+     */
     public Object clone() {
-        try {
-            Flags clone = (Flags) super.clone();
-            // do a deep clone of user_flags
-            clone._map = new HashMap(_map);
-            return clone;
-        } catch (CloneNotSupportedException e) {
-            throw new AssertionError();
-        }
+        return new Flags(this);
     }
 
+    /**
+     * See if the supplied system flags are set
+     * @param flag the system flags to check for
+     * @return true if the flags are set
+     */
     public boolean contains(Flag flag) {
-        return _map.containsKey(flag.getName());
+        return (system_flags & flag.mask) != 0;
     }
 
+    /**
+     * See if all of the supplied Flags are set
+     * @param flags the flags to check for
+     * @return true if all the supplied system and user flags are set
+     */
     public boolean contains(Flags flags) {
-        Iterator it = flags._map.keySet().iterator();
-        boolean result = true;
-        while (result && it.hasNext()) {
-            result = _map.containsKey(it.next());
-        }
-        return result;
+        return ((system_flags & flags.system_flags) == flags.system_flags)
+                && user_flags.keySet().containsAll(flags.user_flags.keySet());
     }
 
+    /**
+     * See if the supplied user flag is set
+     * @param name the user flag to check for
+     * @return true if the flag is set
+     */
     public boolean contains(String name) {
-        return _map.containsKey(name);
+        return user_flags.containsKey(name.toLowerCase());
     }
 
+    /**
+     * Equality is defined as true if the other object is a instanceof Flags with the
+     * same system and user flags set (using a case-insensitive name comparison for user flags).
+     * @param other the instance to compare against
+     * @return true if the two instance are the same
+     */
     public boolean equals(Object other) {
-        if (other == null || other.getClass() != this.getClass()) {
-            return false;
-        }
-        Flags flags = (Flags) other;
-        return flags._map.equals(this._map);
+        if (other == this) return true;
+        if (other instanceof Flags == false) return false;
+        final Flags flags = (Flags) other;
+        return system_flags == flags.system_flags && user_flags.keySet().equals(flags.user_flags.keySet());
     }
 
-    public Flag[] getSystemFlags() {
-        List result = new LinkedList();
-        Iterator it = _map.values().iterator();
-        while (it.hasNext()) {
-            Flag flag = (Flag) it.next();
-            if (flag.isSystemFlag()) {
-                result.add(flag);
-            }
-        }
-        return (Flag[]) result.toArray(FLAG_ARRAY);
+    /**
+     * Calculate a hashCode for this instance
+     * @return a hashCode for this instance
+     */
+    public int hashCode() {
+        return system_flags ^ user_flags.keySet().hashCode();
     }
 
-    public String[] getUserFlags() {
-        List result = new LinkedList();
-        Iterator it = _map.values().iterator();
-        while (it.hasNext()) {
-            Flag flag = (Flag) it.next();
-            if (!flag.isSystemFlag()) {
-                result.add(flag.getName());
-            }
-        }
-        return (String[]) result.toArray(STRING_ARRAY);
+    /**
+     * Return a list of {@link Flags.Flag Flags} containing the system flags that have been set
+     * @return the system flags that have been set
+     */
+    public Flag[] getSystemFlags() {
+        // assumption: it is quicker to calculate the size than it is to reallocate the array
+        int size = 0;
+        if ((system_flags & Flag.ANSWERED.mask) != 0) size += 1;
+        if ((system_flags & Flag.DELETED.mask) != 0) size += 1;
+        if ((system_flags & Flag.DRAFT.mask) != 0) size += 1;
+        if ((system_flags & Flag.FLAGGED.mask) != 0) size += 1;
+        if ((system_flags & Flag.RECENT.mask) != 0) size += 1;
+        if ((system_flags & Flag.SEEN.mask) != 0) size += 1;
+        if ((system_flags & Flag.USER.mask) != 0) size += 1;
+        Flag[] result = new Flag[size];
+        if ((system_flags & Flag.USER.mask) != 0) result[--size] = Flag.USER;
+        if ((system_flags & Flag.SEEN.mask) != 0) result[--size] = Flag.SEEN;
+        if ((system_flags & Flag.RECENT.mask) != 0) result[--size] = Flag.RECENT;
+        if ((system_flags & Flag.FLAGGED.mask) != 0) result[--size] = Flag.FLAGGED;
+        if ((system_flags & Flag.DRAFT.mask) != 0) result[--size] = Flag.DRAFT;
+        if ((system_flags & Flag.DELETED.mask) != 0) result[--size] = Flag.DELETED;
+        if ((system_flags & Flag.ANSWERED.mask) != 0) result[--size] = Flag.ANSWERED;
+        return result;
     }
 
-    public int hashCode() {
-        return _map.keySet().hashCode();
+    /**
+     * Return a list of user flags that have been set
+     * @return a list of user flags
+     */
+    public String[] getUserFlags() {
+        return (String[]) user_flags.values().toArray(new String[user_flags.values().size()]);
     }
 
+    /**
+     * Unset the supplied system flag.
+     * Question: what happens if we unset the USER flags and user flags are set?
+     * @param flag the flag to clear
+     */
     public void remove(Flag flag) {
-        _map.remove(flag.getName());
+        system_flags &= ~flag.mask;
     }
 
+    /**
+     * Unset all flags from the supplied instance.
+     * @param flags the flags to clear
+     */
     public void remove(Flags flags) {
-        Iterator it = flags._map.keySet().iterator();
-        while (it.hasNext()) {
-            _map.remove(it.next());
-        }
+        system_flags &= ~flags.system_flags;
+        user_flags.keySet().removeAll(flags.user_flags.keySet());
     }
 
+    /**
+     * Unset the supplied user flag.
+     * @param name the flag to clear
+     */
     public void remove(String name) {
-        _map.remove(name);
-    }
-
-    public String toString() {
-        return _map.keySet().toString();
+        user_flags.remove(name.toLowerCase());
     }
 }