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 }