001    /**
002     *
003     * Copyright 2003-2004 The Apache Software Foundation
004     *
005     *  Licensed under the Apache License, Version 2.0 (the "License");
006     *  you may not use this file except in compliance with the License.
007     *  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package javax.mail;
019    
020    import java.io.Serializable;
021    import java.util.Hashtable;
022    import java.util.Iterator;
023    import java.util.LinkedList;
024    import java.util.List;
025    
026    /**
027     * Representation of flags that may be associated with a message.
028     * Flags can either be system flags, defined by the {@link Flags.Flag Flag} inner class,
029     * or user-defined flags defined by a String. The system flags represent those expected
030     * to be provided by most folder systems; user-defined flags allow for additional flags
031     * on a per-provider basis.
032     * <p/>
033     * This class is Serializable but compatibility is not guaranteed across releases.
034     *
035     * @version $Rev: 125713 $ $Date: 2005-01-19 21:07:59 -0800 (Wed, 19 Jan 2005) $
036     */
037    public class Flags implements Cloneable, Serializable {
038        public static final class Flag {
039            /**
040             * Flag that indicates that the message has been replied to; has a bit value of 1.
041             */
042            public static final Flag ANSWERED = new Flag(1);
043            /**
044             * Flag that indicates that the message has been marked for deletion and
045             * should be removed on a subsequent expunge operation; has a bit value of 2.
046             */
047            public static final Flag DELETED = new Flag(2);
048            /**
049             * Flag that indicates that the message is a draft; has a bit value of 4.
050             */
051            public static final Flag DRAFT = new Flag(4);
052            /**
053             * Flag that indicates that the message has been flagged; has a bit value of 8.
054             */
055            public static final Flag FLAGGED = new Flag(8);
056            /**
057             * Flag that indicates that the message has been delivered since the last time
058             * this folder was opened; has a bit value of 16.
059             */
060            public static final Flag RECENT = new Flag(16);
061            /**
062             * Flag that indicates that the message has been viewed; has a bit value of 32.
063             * This flag is set by the {@link Message#getInputStream()} and {@link Message#getContent()}
064             * methods.
065             */
066            public static final Flag SEEN = new Flag(32);
067            /**
068             * Flags that indicates if this folder supports user-defined flags; has a bit value of 0x80000000.
069             */
070            public static final Flag USER = new Flag(0x80000000);
071    
072            private final int mask;
073    
074            private Flag(int mask) {
075                this.mask = mask;
076            }
077        }
078    
079        // the Serialized form of this class required the following two fields to be persisted
080        // this leads to a specific type of implementation
081        private int system_flags;
082        private final Hashtable user_flags;
083    
084        /**
085         * Construct a Flags instance with no flags set.
086         */
087        public Flags() {
088            user_flags = new Hashtable();
089        }
090    
091        /**
092         * Construct a Flags instance with a supplied system flag set.
093         * @param flag the system flag to set
094         */
095        public Flags(Flag flag) {
096            system_flags = flag.mask;
097            user_flags = new Hashtable();
098        }
099    
100        /**
101         * Construct a Flags instance with a same flags set.
102         * @param flags the instance to copy
103         */
104        public Flags(Flags flags) {
105            system_flags = flags.system_flags;
106            user_flags = new Hashtable(flags.user_flags);
107        }
108    
109        /**
110         * Construct a Flags instance with the supplied user flags set.
111         * Question: should this automatically set the USER system flag?
112         * @param name the user flag to set
113         */
114        public Flags(String name) {
115            user_flags = new Hashtable();
116            user_flags.put(name.toLowerCase(), name);
117        }
118    
119        /**
120         * Set a system flag.
121         * @param flag the system flag to set
122         */
123        public void add(Flag flag) {
124            system_flags |= flag.mask;
125        }
126    
127        /**
128         * Set all system and user flags from the supplied Flags.
129         * Question: do we need to check compatibility of USER flags?
130         * @param flags the Flags to add
131         */
132        public void add(Flags flags) {
133            system_flags |= flags.system_flags;
134            user_flags.putAll(flags.user_flags);
135        }
136    
137        /**
138         * Set a user flag.
139         * Question: should this fail if the USER system flag is not set?
140         * @param name the user flag to set
141         */
142        public void add(String name) {
143            user_flags.put(name.toLowerCase(), name);
144        }
145    
146        /**
147         * Return a copy of this instance.
148         * @return a copy of this instance
149         */
150        public Object clone() {
151            return new Flags(this);
152        }
153    
154        /**
155         * See if the supplied system flags are set
156         * @param flag the system flags to check for
157         * @return true if the flags are set
158         */
159        public boolean contains(Flag flag) {
160            return (system_flags & flag.mask) != 0;
161        }
162    
163        /**
164         * See if all of the supplied Flags are set
165         * @param flags the flags to check for
166         * @return true if all the supplied system and user flags are set
167         */
168        public boolean contains(Flags flags) {
169            return ((system_flags & flags.system_flags) == flags.system_flags)
170                    && user_flags.keySet().containsAll(flags.user_flags.keySet());
171        }
172    
173        /**
174         * See if the supplied user flag is set
175         * @param name the user flag to check for
176         * @return true if the flag is set
177         */
178        public boolean contains(String name) {
179            return user_flags.containsKey(name.toLowerCase());
180        }
181    
182        /**
183         * Equality is defined as true if the other object is a instanceof Flags with the
184         * same system and user flags set (using a case-insensitive name comparison for user flags).
185         * @param other the instance to compare against
186         * @return true if the two instance are the same
187         */
188        public boolean equals(Object other) {
189            if (other == this) return true;
190            if (other instanceof Flags == false) return false;
191            final Flags flags = (Flags) other;
192            return system_flags == flags.system_flags && user_flags.keySet().equals(flags.user_flags.keySet());
193        }
194    
195        /**
196         * Calculate a hashCode for this instance
197         * @return a hashCode for this instance
198         */
199        public int hashCode() {
200            return system_flags ^ user_flags.keySet().hashCode();
201        }
202    
203        /**
204         * Return a list of {@link Flags.Flag Flags} containing the system flags that have been set
205         * @return the system flags that have been set
206         */
207        public Flag[] getSystemFlags() {
208            // assumption: it is quicker to calculate the size than it is to reallocate the array
209            int size = 0;
210            if ((system_flags & Flag.ANSWERED.mask) != 0) size += 1;
211            if ((system_flags & Flag.DELETED.mask) != 0) size += 1;
212            if ((system_flags & Flag.DRAFT.mask) != 0) size += 1;
213            if ((system_flags & Flag.FLAGGED.mask) != 0) size += 1;
214            if ((system_flags & Flag.RECENT.mask) != 0) size += 1;
215            if ((system_flags & Flag.SEEN.mask) != 0) size += 1;
216            if ((system_flags & Flag.USER.mask) != 0) size += 1;
217            Flag[] result = new Flag[size];
218            if ((system_flags & Flag.USER.mask) != 0) result[--size] = Flag.USER;
219            if ((system_flags & Flag.SEEN.mask) != 0) result[--size] = Flag.SEEN;
220            if ((system_flags & Flag.RECENT.mask) != 0) result[--size] = Flag.RECENT;
221            if ((system_flags & Flag.FLAGGED.mask) != 0) result[--size] = Flag.FLAGGED;
222            if ((system_flags & Flag.DRAFT.mask) != 0) result[--size] = Flag.DRAFT;
223            if ((system_flags & Flag.DELETED.mask) != 0) result[--size] = Flag.DELETED;
224            if ((system_flags & Flag.ANSWERED.mask) != 0) result[--size] = Flag.ANSWERED;
225            return result;
226        }
227    
228        /**
229         * Return a list of user flags that have been set
230         * @return a list of user flags
231         */
232        public String[] getUserFlags() {
233            return (String[]) user_flags.values().toArray(new String[user_flags.values().size()]);
234        }
235    
236        /**
237         * Unset the supplied system flag.
238         * Question: what happens if we unset the USER flags and user flags are set?
239         * @param flag the flag to clear
240         */
241        public void remove(Flag flag) {
242            system_flags &= ~flag.mask;
243        }
244    
245        /**
246         * Unset all flags from the supplied instance.
247         * @param flags the flags to clear
248         */
249        public void remove(Flags flags) {
250            system_flags &= ~flags.system_flags;
251            user_flags.keySet().removeAll(flags.user_flags.keySet());
252        }
253    
254        /**
255         * Unset the supplied user flag.
256         * @param name the flag to clear
257         */
258        public void remove(String name) {
259            user_flags.remove(name.toLowerCase());
260        }
261    }