1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20 package javax.mail; 21 22 import java.io.Serializable; 23 import java.util.Hashtable; 24 import java.util.Iterator; 25 import java.util.LinkedList; 26 import java.util.List; 27 28 /** 29 * Representation of flags that may be associated with a message. 30 * Flags can either be system flags, defined by the {@link Flags.Flag Flag} inner class, 31 * or user-defined flags defined by a String. The system flags represent those expected 32 * to be provided by most folder systems; user-defined flags allow for additional flags 33 * on a per-provider basis. 34 * <p/> 35 * This class is Serializable but compatibility is not guaranteed across releases. 36 * 37 * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ 38 */ 39 public class Flags implements Cloneable, Serializable { 40 public static final class Flag { 41 /** 42 * Flag that indicates that the message has been replied to; has a bit value of 1. 43 */ 44 public static final Flag ANSWERED = new Flag(1); 45 /** 46 * Flag that indicates that the message has been marked for deletion and 47 * should be removed on a subsequent expunge operation; has a bit value of 2. 48 */ 49 public static final Flag DELETED = new Flag(2); 50 /** 51 * Flag that indicates that the message is a draft; has a bit value of 4. 52 */ 53 public static final Flag DRAFT = new Flag(4); 54 /** 55 * Flag that indicates that the message has been flagged; has a bit value of 8. 56 */ 57 public static final Flag FLAGGED = new Flag(8); 58 /** 59 * Flag that indicates that the message has been delivered since the last time 60 * this folder was opened; has a bit value of 16. 61 */ 62 public static final Flag RECENT = new Flag(16); 63 /** 64 * Flag that indicates that the message has been viewed; has a bit value of 32. 65 * This flag is set by the {@link Message#getInputStream()} and {@link Message#getContent()} 66 * methods. 67 */ 68 public static final Flag SEEN = new Flag(32); 69 /** 70 * Flags that indicates if this folder supports user-defined flags; has a bit value of 0x80000000. 71 */ 72 public static final Flag USER = new Flag(0x80000000); 73 74 private final int mask; 75 76 private Flag(int mask) { 77 this.mask = mask; 78 } 79 } 80 81 // the Serialized form of this class required the following two fields to be persisted 82 // this leads to a specific type of implementation 83 private int system_flags; 84 private final Hashtable user_flags; 85 86 /** 87 * Construct a Flags instance with no flags set. 88 */ 89 public Flags() { 90 user_flags = new Hashtable(); 91 } 92 93 /** 94 * Construct a Flags instance with a supplied system flag set. 95 * @param flag the system flag to set 96 */ 97 public Flags(Flag flag) { 98 system_flags = flag.mask; 99 user_flags = new Hashtable(); 100 } 101 102 /** 103 * Construct a Flags instance with a same flags set. 104 * @param flags the instance to copy 105 */ 106 public Flags(Flags flags) { 107 system_flags = flags.system_flags; 108 user_flags = new Hashtable(flags.user_flags); 109 } 110 111 /** 112 * Construct a Flags instance with the supplied user flags set. 113 * Question: should this automatically set the USER system flag? 114 * @param name the user flag to set 115 */ 116 public Flags(String name) { 117 user_flags = new Hashtable(); 118 user_flags.put(name.toLowerCase(), name); 119 } 120 121 /** 122 * Set a system flag. 123 * @param flag the system flag to set 124 */ 125 public void add(Flag flag) { 126 system_flags |= flag.mask; 127 } 128 129 /** 130 * Set all system and user flags from the supplied Flags. 131 * Question: do we need to check compatibility of USER flags? 132 * @param flags the Flags to add 133 */ 134 public void add(Flags flags) { 135 system_flags |= flags.system_flags; 136 user_flags.putAll(flags.user_flags); 137 } 138 139 /** 140 * Set a user flag. 141 * Question: should this fail if the USER system flag is not set? 142 * @param name the user flag to set 143 */ 144 public void add(String name) { 145 user_flags.put(name.toLowerCase(), name); 146 } 147 148 /** 149 * Return a copy of this instance. 150 * @return a copy of this instance 151 */ 152 public Object clone() { 153 return new Flags(this); 154 } 155 156 /** 157 * See if the supplied system flags are set 158 * @param flag the system flags to check for 159 * @return true if the flags are set 160 */ 161 public boolean contains(Flag flag) { 162 return (system_flags & flag.mask) != 0; 163 } 164 165 /** 166 * See if all of the supplied Flags are set 167 * @param flags the flags to check for 168 * @return true if all the supplied system and user flags are set 169 */ 170 public boolean contains(Flags flags) { 171 return ((system_flags & flags.system_flags) == flags.system_flags) 172 && user_flags.keySet().containsAll(flags.user_flags.keySet()); 173 } 174 175 /** 176 * See if the supplied user flag is set 177 * @param name the user flag to check for 178 * @return true if the flag is set 179 */ 180 public boolean contains(String name) { 181 return user_flags.containsKey(name.toLowerCase()); 182 } 183 184 /** 185 * Equality is defined as true if the other object is a instanceof Flags with the 186 * same system and user flags set (using a case-insensitive name comparison for user flags). 187 * @param other the instance to compare against 188 * @return true if the two instance are the same 189 */ 190 public boolean equals(Object other) { 191 if (other == this) return true; 192 if (other instanceof Flags == false) return false; 193 final Flags flags = (Flags) other; 194 return system_flags == flags.system_flags && user_flags.keySet().equals(flags.user_flags.keySet()); 195 } 196 197 /** 198 * Calculate a hashCode for this instance 199 * @return a hashCode for this instance 200 */ 201 public int hashCode() { 202 return system_flags ^ user_flags.keySet().hashCode(); 203 } 204 205 /** 206 * Return a list of {@link Flags.Flag Flags} containing the system flags that have been set 207 * @return the system flags that have been set 208 */ 209 public Flag[] getSystemFlags() { 210 // assumption: it is quicker to calculate the size than it is to reallocate the array 211 int size = 0; 212 if ((system_flags & Flag.ANSWERED.mask) != 0) size += 1; 213 if ((system_flags & Flag.DELETED.mask) != 0) size += 1; 214 if ((system_flags & Flag.DRAFT.mask) != 0) size += 1; 215 if ((system_flags & Flag.FLAGGED.mask) != 0) size += 1; 216 if ((system_flags & Flag.RECENT.mask) != 0) size += 1; 217 if ((system_flags & Flag.SEEN.mask) != 0) size += 1; 218 if ((system_flags & Flag.USER.mask) != 0) size += 1; 219 Flag[] result = new Flag[size]; 220 if ((system_flags & Flag.USER.mask) != 0) result[--size] = Flag.USER; 221 if ((system_flags & Flag.SEEN.mask) != 0) result[--size] = Flag.SEEN; 222 if ((system_flags & Flag.RECENT.mask) != 0) result[--size] = Flag.RECENT; 223 if ((system_flags & Flag.FLAGGED.mask) != 0) result[--size] = Flag.FLAGGED; 224 if ((system_flags & Flag.DRAFT.mask) != 0) result[--size] = Flag.DRAFT; 225 if ((system_flags & Flag.DELETED.mask) != 0) result[--size] = Flag.DELETED; 226 if ((system_flags & Flag.ANSWERED.mask) != 0) result[--size] = Flag.ANSWERED; 227 return result; 228 } 229 230 /** 231 * Return a list of user flags that have been set 232 * @return a list of user flags 233 */ 234 public String[] getUserFlags() { 235 return (String[]) user_flags.values().toArray(new String[user_flags.values().size()]); 236 } 237 238 /** 239 * Unset the supplied system flag. 240 * Question: what happens if we unset the USER flags and user flags are set? 241 * @param flag the flag to clear 242 */ 243 public void remove(Flag flag) { 244 system_flags &= ~flag.mask; 245 } 246 247 /** 248 * Unset all flags from the supplied instance. 249 * @param flags the flags to clear 250 */ 251 public void remove(Flags flags) { 252 system_flags &= ~flags.system_flags; 253 user_flags.keySet().removeAll(flags.user_flags.keySet()); 254 } 255 256 /** 257 * Unset the supplied user flag. 258 * @param name the flag to clear 259 */ 260 public void remove(String name) { 261 user_flags.remove(name.toLowerCase()); 262 } 263 }