View Javadoc

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 }