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 [EMAIL PROTECTED] 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 [EMAIL PROTECTED] Message#getInputStream()} and [EMAIL PROTECTED] 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 [EMAIL PROTECTED] 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()); } }