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: 920714 $ $Date: 2010-03-09 01:55:49 -0500 (Tue, 09 Mar 2010) $
038 */
039 public class Flags implements Cloneable, Serializable {
040
041 private static final long serialVersionUID = 6243590407214169028L;
042
043 public static final class Flag {
044 /**
045 * Flag that indicates that the message has been replied to; has a bit value of 1.
046 */
047 public static final Flag ANSWERED = new Flag(1);
048 /**
049 * Flag that indicates that the message has been marked for deletion and
050 * should be removed on a subsequent expunge operation; has a bit value of 2.
051 */
052 public static final Flag DELETED = new Flag(2);
053 /**
054 * Flag that indicates that the message is a draft; has a bit value of 4.
055 */
056 public static final Flag DRAFT = new Flag(4);
057 /**
058 * Flag that indicates that the message has been flagged; has a bit value of 8.
059 */
060 public static final Flag FLAGGED = new Flag(8);
061 /**
062 * Flag that indicates that the message has been delivered since the last time
063 * this folder was opened; has a bit value of 16.
064 */
065 public static final Flag RECENT = new Flag(16);
066 /**
067 * Flag that indicates that the message has been viewed; has a bit value of 32.
068 * This flag is set by the {@link Message#getInputStream()} and {@link Message#getContent()}
069 * methods.
070 */
071 public static final Flag SEEN = new Flag(32);
072 /**
073 * Flags that indicates if this folder supports user-defined flags; has a bit value of 0x80000000.
074 */
075 public static final Flag USER = new Flag(0x80000000);
076
077 private final int mask;
078
079 private Flag(int mask) {
080 this.mask = mask;
081 }
082 }
083
084 // the Serialized form of this class required the following two fields to be persisted
085 // this leads to a specific type of implementation
086 private int system_flags;
087 private final Hashtable user_flags;
088
089 /**
090 * Construct a Flags instance with no flags set.
091 */
092 public Flags() {
093 user_flags = new Hashtable();
094 }
095
096 /**
097 * Construct a Flags instance with a supplied system flag set.
098 * @param flag the system flag to set
099 */
100 public Flags(Flag flag) {
101 system_flags = flag.mask;
102 user_flags = new Hashtable();
103 }
104
105 /**
106 * Construct a Flags instance with a same flags set.
107 * @param flags the instance to copy
108 */
109 public Flags(Flags flags) {
110 system_flags = flags.system_flags;
111 user_flags = new Hashtable(flags.user_flags);
112 }
113
114 /**
115 * Construct a Flags instance with the supplied user flags set.
116 * Question: should this automatically set the USER system flag?
117 * @param name the user flag to set
118 */
119 public Flags(String name) {
120 user_flags = new Hashtable();
121 user_flags.put(name.toLowerCase(), name);
122 }
123
124 /**
125 * Set a system flag.
126 * @param flag the system flag to set
127 */
128 public void add(Flag flag) {
129 system_flags |= flag.mask;
130 }
131
132 /**
133 * Set all system and user flags from the supplied Flags.
134 * Question: do we need to check compatibility of USER flags?
135 * @param flags the Flags to add
136 */
137 public void add(Flags flags) {
138 system_flags |= flags.system_flags;
139 user_flags.putAll(flags.user_flags);
140 }
141
142 /**
143 * Set a user flag.
144 * Question: should this fail if the USER system flag is not set?
145 * @param name the user flag to set
146 */
147 public void add(String name) {
148 user_flags.put(name.toLowerCase(), name);
149 }
150
151 /**
152 * Return a copy of this instance.
153 * @return a copy of this instance
154 */
155 public Object clone() {
156 return new Flags(this);
157 }
158
159 /**
160 * See if the supplied system flags are set
161 * @param flag the system flags to check for
162 * @return true if the flags are set
163 */
164 public boolean contains(Flag flag) {
165 return (system_flags & flag.mask) != 0;
166 }
167
168 /**
169 * See if all of the supplied Flags are set
170 * @param flags the flags to check for
171 * @return true if all the supplied system and user flags are set
172 */
173 public boolean contains(Flags flags) {
174 return ((system_flags & flags.system_flags) == flags.system_flags)
175 && user_flags.keySet().containsAll(flags.user_flags.keySet());
176 }
177
178 /**
179 * See if the supplied user flag is set
180 * @param name the user flag to check for
181 * @return true if the flag is set
182 */
183 public boolean contains(String name) {
184 return user_flags.containsKey(name.toLowerCase());
185 }
186
187 /**
188 * Equality is defined as true if the other object is a instanceof Flags with the
189 * same system and user flags set (using a case-insensitive name comparison for user flags).
190 * @param other the instance to compare against
191 * @return true if the two instance are the same
192 */
193 public boolean equals(Object other) {
194 if (other == this) return true;
195 if (other instanceof Flags == false) return false;
196 final Flags flags = (Flags) other;
197 return system_flags == flags.system_flags && user_flags.keySet().equals(flags.user_flags.keySet());
198 }
199
200 /**
201 * Calculate a hashCode for this instance
202 * @return a hashCode for this instance
203 */
204 public int hashCode() {
205 return system_flags ^ user_flags.keySet().hashCode();
206 }
207
208 /**
209 * Return a list of {@link Flags.Flag Flags} containing the system flags that have been set
210 * @return the system flags that have been set
211 */
212 public Flag[] getSystemFlags() {
213 // assumption: it is quicker to calculate the size than it is to reallocate the array
214 int size = 0;
215 if ((system_flags & Flag.ANSWERED.mask) != 0) size += 1;
216 if ((system_flags & Flag.DELETED.mask) != 0) size += 1;
217 if ((system_flags & Flag.DRAFT.mask) != 0) size += 1;
218 if ((system_flags & Flag.FLAGGED.mask) != 0) size += 1;
219 if ((system_flags & Flag.RECENT.mask) != 0) size += 1;
220 if ((system_flags & Flag.SEEN.mask) != 0) size += 1;
221 if ((system_flags & Flag.USER.mask) != 0) size += 1;
222 Flag[] result = new Flag[size];
223 if ((system_flags & Flag.USER.mask) != 0) result[--size] = Flag.USER;
224 if ((system_flags & Flag.SEEN.mask) != 0) result[--size] = Flag.SEEN;
225 if ((system_flags & Flag.RECENT.mask) != 0) result[--size] = Flag.RECENT;
226 if ((system_flags & Flag.FLAGGED.mask) != 0) result[--size] = Flag.FLAGGED;
227 if ((system_flags & Flag.DRAFT.mask) != 0) result[--size] = Flag.DRAFT;
228 if ((system_flags & Flag.DELETED.mask) != 0) result[--size] = Flag.DELETED;
229 if ((system_flags & Flag.ANSWERED.mask) != 0) result[--size] = Flag.ANSWERED;
230 return result;
231 }
232
233 /**
234 * Return a list of user flags that have been set
235 * @return a list of user flags
236 */
237 public String[] getUserFlags() {
238 return (String[]) user_flags.values().toArray(new String[user_flags.values().size()]);
239 }
240
241 /**
242 * Unset the supplied system flag.
243 * Question: what happens if we unset the USER flags and user flags are set?
244 * @param flag the flag to clear
245 */
246 public void remove(Flag flag) {
247 system_flags &= ~flag.mask;
248 }
249
250 /**
251 * Unset all flags from the supplied instance.
252 * @param flags the flags to clear
253 */
254 public void remove(Flags flags) {
255 system_flags &= ~flags.system_flags;
256 user_flags.keySet().removeAll(flags.user_flags.keySet());
257 }
258
259 /**
260 * Unset the supplied user flag.
261 * @param name the flag to clear
262 */
263 public void remove(String name) {
264 user_flags.remove(name.toLowerCase());
265 }
266 }