001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020 package javax.mail; 021 022 import java.io.Serializable; 023 import java.util.Hashtable; 024 import java.util.Iterator; 025 import java.util.LinkedList; 026 import java.util.List; 027 028 /** 029 * Representation of flags that may be associated with a message. 030 * Flags can either be system flags, defined by the {@link Flags.Flag Flag} inner class, 031 * or user-defined flags defined by a String. The system flags represent those expected 032 * to be provided by most folder systems; user-defined flags allow for additional flags 033 * on a per-provider basis. 034 * <p/> 035 * This class is Serializable but compatibility is not guaranteed across releases. 036 * 037 * @version $Rev: 467553 $ $Date: 2006-10-25 00:01:51 -0400 (Wed, 25 Oct 2006) $ 038 */ 039 public class Flags implements Cloneable, Serializable { 040 public static final class Flag { 041 /** 042 * Flag that indicates that the message has been replied to; has a bit value of 1. 043 */ 044 public static final Flag ANSWERED = new Flag(1); 045 /** 046 * Flag that indicates that the message has been marked for deletion and 047 * should be removed on a subsequent expunge operation; has a bit value of 2. 048 */ 049 public static final Flag DELETED = new Flag(2); 050 /** 051 * Flag that indicates that the message is a draft; has a bit value of 4. 052 */ 053 public static final Flag DRAFT = new Flag(4); 054 /** 055 * Flag that indicates that the message has been flagged; has a bit value of 8. 056 */ 057 public static final Flag FLAGGED = new Flag(8); 058 /** 059 * Flag that indicates that the message has been delivered since the last time 060 * this folder was opened; has a bit value of 16. 061 */ 062 public static final Flag RECENT = new Flag(16); 063 /** 064 * Flag that indicates that the message has been viewed; has a bit value of 32. 065 * This flag is set by the {@link Message#getInputStream()} and {@link Message#getContent()} 066 * methods. 067 */ 068 public static final Flag SEEN = new Flag(32); 069 /** 070 * Flags that indicates if this folder supports user-defined flags; has a bit value of 0x80000000. 071 */ 072 public static final Flag USER = new Flag(0x80000000); 073 074 private final int mask; 075 076 private Flag(int mask) { 077 this.mask = mask; 078 } 079 } 080 081 // the Serialized form of this class required the following two fields to be persisted 082 // this leads to a specific type of implementation 083 private int system_flags; 084 private final Hashtable user_flags; 085 086 /** 087 * Construct a Flags instance with no flags set. 088 */ 089 public Flags() { 090 user_flags = new Hashtable(); 091 } 092 093 /** 094 * Construct a Flags instance with a supplied system flag set. 095 * @param flag the system flag to set 096 */ 097 public Flags(Flag flag) { 098 system_flags = flag.mask; 099 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 }