001 /** 002 * 003 * Copyright 2003-2004 The Apache Software Foundation 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package javax.mail; 019 020 import java.io.Serializable; 021 import java.util.Hashtable; 022 import java.util.Iterator; 023 import java.util.LinkedList; 024 import java.util.List; 025 026 /** 027 * Representation of flags that may be associated with a message. 028 * Flags can either be system flags, defined by the {@link Flags.Flag Flag} inner class, 029 * or user-defined flags defined by a String. The system flags represent those expected 030 * to be provided by most folder systems; user-defined flags allow for additional flags 031 * on a per-provider basis. 032 * <p/> 033 * This class is Serializable but compatibility is not guaranteed across releases. 034 * 035 * @version $Rev: 125713 $ $Date: 2005-01-19 21:07:59 -0800 (Wed, 19 Jan 2005) $ 036 */ 037 public class Flags implements Cloneable, Serializable { 038 public static final class Flag { 039 /** 040 * Flag that indicates that the message has been replied to; has a bit value of 1. 041 */ 042 public static final Flag ANSWERED = new Flag(1); 043 /** 044 * Flag that indicates that the message has been marked for deletion and 045 * should be removed on a subsequent expunge operation; has a bit value of 2. 046 */ 047 public static final Flag DELETED = new Flag(2); 048 /** 049 * Flag that indicates that the message is a draft; has a bit value of 4. 050 */ 051 public static final Flag DRAFT = new Flag(4); 052 /** 053 * Flag that indicates that the message has been flagged; has a bit value of 8. 054 */ 055 public static final Flag FLAGGED = new Flag(8); 056 /** 057 * Flag that indicates that the message has been delivered since the last time 058 * this folder was opened; has a bit value of 16. 059 */ 060 public static final Flag RECENT = new Flag(16); 061 /** 062 * Flag that indicates that the message has been viewed; has a bit value of 32. 063 * This flag is set by the {@link Message#getInputStream()} and {@link Message#getContent()} 064 * methods. 065 */ 066 public static final Flag SEEN = new Flag(32); 067 /** 068 * Flags that indicates if this folder supports user-defined flags; has a bit value of 0x80000000. 069 */ 070 public static final Flag USER = new Flag(0x80000000); 071 072 private final int mask; 073 074 private Flag(int mask) { 075 this.mask = mask; 076 } 077 } 078 079 // the Serialized form of this class required the following two fields to be persisted 080 // this leads to a specific type of implementation 081 private int system_flags; 082 private final Hashtable user_flags; 083 084 /** 085 * Construct a Flags instance with no flags set. 086 */ 087 public Flags() { 088 user_flags = new Hashtable(); 089 } 090 091 /** 092 * Construct a Flags instance with a supplied system flag set. 093 * @param flag the system flag to set 094 */ 095 public Flags(Flag flag) { 096 system_flags = flag.mask; 097 user_flags = new Hashtable(); 098 } 099 100 /** 101 * Construct a Flags instance with a same flags set. 102 * @param flags the instance to copy 103 */ 104 public Flags(Flags flags) { 105 system_flags = flags.system_flags; 106 user_flags = new Hashtable(flags.user_flags); 107 } 108 109 /** 110 * Construct a Flags instance with the supplied user flags set. 111 * Question: should this automatically set the USER system flag? 112 * @param name the user flag to set 113 */ 114 public Flags(String name) { 115 user_flags = new Hashtable(); 116 user_flags.put(name.toLowerCase(), name); 117 } 118 119 /** 120 * Set a system flag. 121 * @param flag the system flag to set 122 */ 123 public void add(Flag flag) { 124 system_flags |= flag.mask; 125 } 126 127 /** 128 * Set all system and user flags from the supplied Flags. 129 * Question: do we need to check compatibility of USER flags? 130 * @param flags the Flags to add 131 */ 132 public void add(Flags flags) { 133 system_flags |= flags.system_flags; 134 user_flags.putAll(flags.user_flags); 135 } 136 137 /** 138 * Set a user flag. 139 * Question: should this fail if the USER system flag is not set? 140 * @param name the user flag to set 141 */ 142 public void add(String name) { 143 user_flags.put(name.toLowerCase(), name); 144 } 145 146 /** 147 * Return a copy of this instance. 148 * @return a copy of this instance 149 */ 150 public Object clone() { 151 return new Flags(this); 152 } 153 154 /** 155 * See if the supplied system flags are set 156 * @param flag the system flags to check for 157 * @return true if the flags are set 158 */ 159 public boolean contains(Flag flag) { 160 return (system_flags & flag.mask) != 0; 161 } 162 163 /** 164 * See if all of the supplied Flags are set 165 * @param flags the flags to check for 166 * @return true if all the supplied system and user flags are set 167 */ 168 public boolean contains(Flags flags) { 169 return ((system_flags & flags.system_flags) == flags.system_flags) 170 && user_flags.keySet().containsAll(flags.user_flags.keySet()); 171 } 172 173 /** 174 * See if the supplied user flag is set 175 * @param name the user flag to check for 176 * @return true if the flag is set 177 */ 178 public boolean contains(String name) { 179 return user_flags.containsKey(name.toLowerCase()); 180 } 181 182 /** 183 * Equality is defined as true if the other object is a instanceof Flags with the 184 * same system and user flags set (using a case-insensitive name comparison for user flags). 185 * @param other the instance to compare against 186 * @return true if the two instance are the same 187 */ 188 public boolean equals(Object other) { 189 if (other == this) return true; 190 if (other instanceof Flags == false) return false; 191 final Flags flags = (Flags) other; 192 return system_flags == flags.system_flags && user_flags.keySet().equals(flags.user_flags.keySet()); 193 } 194 195 /** 196 * Calculate a hashCode for this instance 197 * @return a hashCode for this instance 198 */ 199 public int hashCode() { 200 return system_flags ^ user_flags.keySet().hashCode(); 201 } 202 203 /** 204 * Return a list of {@link Flags.Flag Flags} containing the system flags that have been set 205 * @return the system flags that have been set 206 */ 207 public Flag[] getSystemFlags() { 208 // assumption: it is quicker to calculate the size than it is to reallocate the array 209 int size = 0; 210 if ((system_flags & Flag.ANSWERED.mask) != 0) size += 1; 211 if ((system_flags & Flag.DELETED.mask) != 0) size += 1; 212 if ((system_flags & Flag.DRAFT.mask) != 0) size += 1; 213 if ((system_flags & Flag.FLAGGED.mask) != 0) size += 1; 214 if ((system_flags & Flag.RECENT.mask) != 0) size += 1; 215 if ((system_flags & Flag.SEEN.mask) != 0) size += 1; 216 if ((system_flags & Flag.USER.mask) != 0) size += 1; 217 Flag[] result = new Flag[size]; 218 if ((system_flags & Flag.USER.mask) != 0) result[--size] = Flag.USER; 219 if ((system_flags & Flag.SEEN.mask) != 0) result[--size] = Flag.SEEN; 220 if ((system_flags & Flag.RECENT.mask) != 0) result[--size] = Flag.RECENT; 221 if ((system_flags & Flag.FLAGGED.mask) != 0) result[--size] = Flag.FLAGGED; 222 if ((system_flags & Flag.DRAFT.mask) != 0) result[--size] = Flag.DRAFT; 223 if ((system_flags & Flag.DELETED.mask) != 0) result[--size] = Flag.DELETED; 224 if ((system_flags & Flag.ANSWERED.mask) != 0) result[--size] = Flag.ANSWERED; 225 return result; 226 } 227 228 /** 229 * Return a list of user flags that have been set 230 * @return a list of user flags 231 */ 232 public String[] getUserFlags() { 233 return (String[]) user_flags.values().toArray(new String[user_flags.values().size()]); 234 } 235 236 /** 237 * Unset the supplied system flag. 238 * Question: what happens if we unset the USER flags and user flags are set? 239 * @param flag the flag to clear 240 */ 241 public void remove(Flag flag) { 242 system_flags &= ~flag.mask; 243 } 244 245 /** 246 * Unset all flags from the supplied instance. 247 * @param flags the flags to clear 248 */ 249 public void remove(Flags flags) { 250 system_flags &= ~flags.system_flags; 251 user_flags.keySet().removeAll(flags.user_flags.keySet()); 252 } 253 254 /** 255 * Unset the supplied user flag. 256 * @param name the flag to clear 257 */ 258 public void remove(String name) { 259 user_flags.remove(name.toLowerCase()); 260 } 261 }