Skip to contentPackage: Flags
Flags
Coverage
1: /*
2: * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package jakarta.mail;
18:
19: import java.io.Serializable;
20: import java.util.*;
21:
22: /**
23: * The Flags class represents the set of flags on a Message. Flags
24: * are composed of predefined system flags, and user defined flags. <p>
25: *
26: * A System flag is represented by the <code>Flags.Flag</code>
27: * inner class. A User defined flag is represented as a String.
28: * User flags are case-independent. <p>
29: *
30: * A set of standard system flags are predefined. Most folder
31: * implementations are expected to support these flags. Some
32: * implementations may also support arbitrary user-defined flags. The
33: * <code>getPermanentFlags</code> method on a Folder returns a Flags
34: * object that holds all the flags that are supported by that folder
35: * implementation. <p>
36: *
37: * A Flags object is serializable so that (for example) the
38: * use of Flags objects in search terms can be serialized
39: * along with the search terms. <p>
40: *
41: * <strong>Warning:</strong>
42: * Serialized objects of this class may not be compatible with future
43: * Jakarta Mail API releases. The current serialization support is
44: * appropriate for short term storage. <p>
45: *
46: * The below code sample illustrates how to set, examine, and get the
47: * flags for a message.
48: * <pre>
49: *
50: * Message m = folder.getMessage(1);
51: * m.setFlag(Flags.Flag.DELETED, true); // set the DELETED flag
52: *
53: * // Check if DELETED flag is set on this message
54: * if (m.isSet(Flags.Flag.DELETED))
55: *        System.out.println("DELETED message");
56: *
57: * // Examine ALL system flags for this message
58: * Flags flags = m.getFlags();
59: * Flags.Flag[] sf = flags.getSystemFlags();
60: * for (int i = 0; i < sf.length; i++) {
61: *        if (sf[i] == Flags.Flag.DELETED)
62: * System.out.println("DELETED message");
63: *        else if (sf[i] == Flags.Flag.SEEN)
64: * System.out.println("SEEN message");
65: * ......
66: * ......
67: * }
68: * </pre>
69: *
70: * @see Folder#getPermanentFlags
71: * @author John Mani
72: * @author Bill Shannon
73: */
74:
75: public class Flags implements Cloneable, Serializable {
76:
77: private int system_flags = 0;
78: // used as a case-independent Set that preserves the original case,
79: // the key is the lowercase flag name and the value is the original
80: private Hashtable<String, String> user_flags = null;
81:
82: private final static int ANSWERED_BIT         = 0x01;
83: private final static int DELETED_BIT         = 0x02;
84: private final static int DRAFT_BIT                 = 0x04;
85: private final static int FLAGGED_BIT         = 0x08;
86: private final static int RECENT_BIT                = 0x10;
87: private final static int SEEN_BIT                = 0x20;
88: private final static int USER_BIT                = 0x80000000;
89:
90: private static final long serialVersionUID = 6243590407214169028L;
91:
92: /**
93: * This inner class represents an individual system flag. A set
94: * of standard system flag objects are predefined here.
95: */
96: public static final class Flag {
97:         /**
98:          * This message has been answered. This flag is set by clients
99:          * to indicate that this message has been answered to.
100:          */
101:         public static final Flag ANSWERED = new Flag(ANSWERED_BIT);
102:
103:         /**
104:          * This message is marked deleted. Clients set this flag to
105:          * mark a message as deleted. The expunge operation on a folder
106:          * removes all messages in that folder that are marked for deletion.
107:          */
108:         public static final Flag DELETED = new Flag(DELETED_BIT);
109:
110:         /**
111:          * This message is a draft. This flag is set by clients
112:          * to indicate that the message is a draft message.
113:          */
114:         public static final Flag DRAFT = new Flag(DRAFT_BIT);
115:
116:         /**
117:          * This message is flagged. No semantic is defined for this flag.
118:          * Clients alter this flag.
119:          */
120:         public static final Flag FLAGGED = new Flag(FLAGGED_BIT);
121:
122:         /**
123:          * This message is recent. Folder implementations set this flag
124:          * to indicate that this message is new to this folder, that is,
125:          * it has arrived since the last time this folder was opened. <p>
126:          *
127:          * Clients cannot alter this flag.
128:          */
129:         public static final Flag RECENT = new Flag(RECENT_BIT);
130:
131:         /**
132:          * This message is seen. This flag is implicitly set by the
133:          * implementation when this Message's content is returned
134:          * to the client in some form. The <code>getInputStream</code>
135:          * and <code>getContent</code> methods on Message cause this
136:          * flag to be set. <p>
137:          *
138:          * Clients can alter this flag.
139:          */
140:         public static final Flag SEEN = new Flag(SEEN_BIT);
141:
142:         /**
143:          * A special flag that indicates that this folder supports
144:          * user defined flags. <p>
145:          *
146:          * The implementation sets this flag. Clients cannot alter
147:          * this flag but can use it to determine if a folder supports
148:          * user defined flags by using
149:          * <code>folder.getPermanentFlags().contains(Flags.Flag.USER)</code>.
150:          */
151:         public static final Flag USER = new Flag(USER_BIT);
152:
153:         // flags are stored as bits for efficiency
154:         private int bit;
155:         private Flag(int bit) {
156:          this.bit = bit;
157:         }
158: }
159:
160:
161: /**
162: * Construct an empty Flags object.
163: */
164: public Flags() { }
165:
166: /**
167: * Construct a Flags object initialized with the given flags.
168: *
169: * @param flags        the flags for initialization
170: */
171: @SuppressWarnings("unchecked")
172: public Flags(Flags flags) {
173:         this.system_flags = flags.system_flags;
174:•        if (flags.user_flags != null)
175:          this.user_flags = (Hashtable)flags.user_flags.clone();
176: }
177:
178: /**
179: * Construct a Flags object initialized with the given system flag.
180: *
181: * @param flag        the flag for initialization
182: */
183: public Flags(Flag flag) {
184:         this.system_flags |= flag.bit;
185: }
186:
187: /**
188: * Construct a Flags object initialized with the given user flag.
189: *
190: * @param flag        the flag for initialization
191: */
192: public Flags(String flag) {
193:         user_flags = new Hashtable<>(1);
194:         user_flags.put(flag.toLowerCase(Locale.ENGLISH), flag);
195: }
196:
197: /**
198: * Add the specified system flag to this Flags object.
199: *
200: * @param flag        the flag to add
201: */
202: public void add(Flag flag) {
203:         system_flags |= flag.bit;
204: }
205:
206: /**
207: * Add the specified user flag to this Flags object.
208: *
209: * @param flag        the flag to add
210: */
211: public void add(String flag) {
212:•        if (user_flags == null)
213:          user_flags = new Hashtable<>(1);
214:         user_flags.put(flag.toLowerCase(Locale.ENGLISH), flag);
215: }
216:
217: /**
218: * Add all the flags in the given Flags object to this
219: * Flags object.
220: *
221: * @param f        Flags object
222: */
223: public void add(Flags f) {
224:         system_flags |= f.system_flags; // add system flags
225:
226:•        if (f.user_flags != null) { // add user-defined flags
227:•         if (user_flags == null)
228:                 user_flags = new Hashtable<>(1);
229:
230:          Enumeration<String> e = f.user_flags.keys();
231:
232:•         while (e.hasMoreElements()) {
233:                 String s = e.nextElement();
234:                 user_flags.put(s, f.user_flags.get(s));
235:          }
236:         }
237: }
238:
239: /**
240: * Remove the specified system flag from this Flags object.
241: *
242: * @param        flag         the flag to be removed
243: */
244: public void remove(Flag flag) {
245:         system_flags &= ~flag.bit;
246: }
247:
248: /**
249: * Remove the specified user flag from this Flags object.
250: *
251: * @param        flag         the flag to be removed
252: */
253: public void remove(String flag) {
254:•        if (user_flags != null)
255:          user_flags.remove(flag.toLowerCase(Locale.ENGLISH));
256: }
257:
258: /**
259: * Remove all flags in the given Flags object from this
260: * Flags object.
261: *
262: * @param        f         the flag to be removed
263: */
264: public void remove(Flags f) {
265:         system_flags &= ~f.system_flags; // remove system flags
266:
267:•        if (f.user_flags != null) {
268:•         if (user_flags == null)
269:                 return;
270:
271:          Enumeration<String> e = f.user_flags.keys();
272:•         while (e.hasMoreElements())
273:                 user_flags.remove(e.nextElement());
274:         }
275: }
276:
277: /**
278: * Remove any flags <strong>not</strong> in the given Flags object.
279: * Useful for clearing flags not supported by a server. If the
280: * given Flags object includes the Flags.Flag.USER flag, all user
281: * flags in this Flags object are retained.
282: *
283: * @param        f        the flags to keep
284: * @return                true if this Flags object changed
285: * @since                JavaMail 1.6
286: */
287: public boolean retainAll(Flags f) {
288:         boolean changed = false;
289:         int sf = system_flags & f.system_flags;
290:•        if (system_flags != sf) {
291:          system_flags = sf;
292:          changed = true;
293:         }
294:
295:         // if we have user flags, and the USER flag is not set in "f",
296:         // determine which user flags to clear
297:•        if (user_flags != null && (f.system_flags & USER_BIT) == 0) {
298:•         if (f.user_flags != null) {
299:                 Enumeration<String> e = user_flags.keys();
300:•                while (e.hasMoreElements()) {
301:                  String key = e.nextElement();
302:•                 if (!f.user_flags.containsKey(key)) {
303:                         user_flags.remove(key);
304:                         changed = true;
305:                  }
306:                 }
307:          } else {
308:                 // if anything in user_flags, throw them away
309:•                changed = user_flags.size() > 0;
310:                 user_flags = null;
311:          }
312:         }
313:         return changed;
314: }
315:
316: /**
317: * Check whether the specified system flag is present in this Flags object.
318: *
319: * @param        flag        the flag to test
320: * @return                 true of the given flag is present, otherwise false.
321: */
322: public boolean contains(Flag flag) {
323:•        return (system_flags & flag.bit) != 0;
324: }
325:
326: /**
327: * Check whether the specified user flag is present in this Flags object.
328: *
329: * @param        flag        the flag to test
330: * @return                 true of the given flag is present, otherwise false.
331: */
332: public boolean contains(String flag) {
333:•        if (user_flags == null)
334:          return false;
335:         else
336:          return user_flags.containsKey(flag.toLowerCase(Locale.ENGLISH));
337: }
338:
339: /**
340: * Check whether all the flags in the specified Flags object are
341: * present in this Flags object.
342: *
343: * @param        f        the flags to test
344: * @return        true if all flags in the given Flags object are present,
345: *                otherwise false.
346: */
347: public boolean contains(Flags f) {
348:         // Check system flags
349:•        if ((f.system_flags & system_flags) != f.system_flags)
350:          return false;
351:
352:         // Check user flags
353:•        if (f.user_flags != null) {
354:•         if (user_flags == null)
355:                 return false;
356:          Enumeration<String> e = f.user_flags.keys();
357:
358:•         while (e.hasMoreElements()) {
359:•                if (!user_flags.containsKey(e.nextElement()))
360:                  return false;
361:          }
362:         }
363:
364:         // If we've made it till here, return true
365:         return true;
366: }
367:
368: /**
369: * Check whether the two Flags objects are equal.
370: *
371: * @return        true if they're equal
372: */
373: @Override
374: public boolean equals(Object obj) {
375:•        if (!(obj instanceof Flags))
376:          return false;
377:
378:         Flags f = (Flags)obj;
379:
380:         // Check system flags
381:•        if (f.system_flags != this.system_flags)
382:          return false;
383:
384:         // Check user flags
385:•        int size = this.user_flags == null ? 0 : this.user_flags.size();
386:•        int fsize = f.user_flags == null ? 0 : f.user_flags.size();
387:•        if (size == 0 && fsize == 0)
388:          return true;
389:•        if (f.user_flags != null && this.user_flags != null && fsize == size)
390:          return user_flags.keySet().equals(f.user_flags.keySet());
391:
392:         return false;
393: }
394:
395: /**
396: * Compute a hash code for this Flags object.
397: *
398: * @return        the hash code
399: */
400: @Override
401: public int hashCode() {
402:         int hash = system_flags;
403:•        if (user_flags != null) {
404:          Enumeration<String> e = user_flags.keys();
405:•         while (e.hasMoreElements())
406:                 hash += e.nextElement().hashCode();
407:         }
408:         return hash;
409: }
410:
411: /**
412: * Return all the system flags in this Flags object. Returns
413: * an array of size zero if no flags are set.
414: *
415: * @return        array of Flags.Flag objects representing system flags
416: */
417: public Flag[] getSystemFlags() {
418:         Vector<Flag> v = new Vector<>();
419:•        if ((system_flags & ANSWERED_BIT) != 0)
420:          v.addElement(Flag.ANSWERED);
421:•        if ((system_flags & DELETED_BIT) != 0)
422:          v.addElement(Flag.DELETED);
423:•        if ((system_flags & DRAFT_BIT) != 0)
424:          v.addElement(Flag.DRAFT);
425:•        if ((system_flags & FLAGGED_BIT) != 0)
426:          v.addElement(Flag.FLAGGED);
427:•        if ((system_flags & RECENT_BIT) != 0)
428:          v.addElement(Flag.RECENT);
429:•        if ((system_flags & SEEN_BIT) != 0)
430:          v.addElement(Flag.SEEN);
431:•        if ((system_flags & USER_BIT) != 0)
432:          v.addElement(Flag.USER);
433:
434:         Flag[] f = new Flag[v.size()];
435:         v.copyInto(f);
436:         return f;
437: }
438:
439: /**
440: * Return all the user flags in this Flags object. Returns
441: * an array of size zero if no flags are set.
442: *
443: * @return        array of Strings, each String represents a flag.
444: */
445: public String[] getUserFlags() {
446:         Vector<String> v = new Vector<>();
447:•        if (user_flags != null) {
448:          Enumeration<String> e = user_flags.elements();
449:
450:•         while (e.hasMoreElements())
451:                 v.addElement(e.nextElement());
452:         }
453:
454:         String[] f = new String[v.size()];
455:         v.copyInto(f);
456:         return f;
457: }
458:
459: /**
460: * Clear all of the system flags.
461: *
462: * @since        JavaMail 1.6
463: */
464: public void clearSystemFlags() {
465:         system_flags = 0;
466: }
467:
468: /**
469: * Clear all of the user flags.
470: *
471: * @since        JavaMail 1.6
472: */
473: public void clearUserFlags() {
474:         user_flags = null;
475: }
476:
477: /**
478: * Returns a clone of this Flags object.
479: */
480: @SuppressWarnings("unchecked")
481: @Override
482: public Object clone() {
483:         Flags f = null;
484:         try {
485:          f = (Flags)super.clone();
486:         } catch (CloneNotSupportedException cex) {
487:          // ignore, can't happen
488:         }
489:•        if (this.user_flags != null)
490:          f.user_flags = (Hashtable)this.user_flags.clone();
491:         return f;
492: }
493:
494: /**
495: * Return a string representation of this Flags object.
496: * Note that the exact format of the string is subject to change.
497: */
498: public String toString() {
499:         StringBuilder sb = new StringBuilder();
500:
501:•        if ((system_flags & ANSWERED_BIT) != 0)
502:          sb.append("\\Answered ");
503:•        if ((system_flags & DELETED_BIT) != 0)
504:          sb.append("\\Deleted ");
505:•        if ((system_flags & DRAFT_BIT) != 0)
506:          sb.append("\\Draft ");
507:•        if ((system_flags & FLAGGED_BIT) != 0)
508:          sb.append("\\Flagged ");
509:•        if ((system_flags & RECENT_BIT) != 0)
510:          sb.append("\\Recent ");
511:•        if ((system_flags & SEEN_BIT) != 0)
512:          sb.append("\\Seen ");
513:•        if ((system_flags & USER_BIT) != 0)
514:          sb.append("\\* ");
515:
516:         boolean first = true;
517:•        if (user_flags != null) {
518:          Enumeration<String> e = user_flags.elements();
519:
520:•         while (e.hasMoreElements()) {
521:•                if (first)
522:                  first = false;
523:                 else
524:                  sb.append(' ');
525:                 sb.append(e.nextElement());
526:          }
527:         }
528:
529:•        if (first && sb.length() > 0)
530:          sb.setLength(sb.length() - 1);        // smash trailing space
531:
532:         return sb.toString();
533: }
534:
535: /*****
536: public static void main(String argv[]) throws Exception {
537:         // a new flags object
538:         Flags f1 = new Flags();
539:         f1.add(Flags.Flag.DELETED);
540:         f1.add(Flags.Flag.SEEN);
541:         f1.add(Flags.Flag.RECENT);
542:         f1.add(Flags.Flag.ANSWERED);
543:
544:         // check copy constructor with only system flags
545:         Flags fc = new Flags(f1);
546:         if (f1.equals(fc) && fc.equals(f1))
547:          System.out.println("success");
548:         else
549:          System.out.println("fail");
550:
551:         // check clone with only system flags
552:         fc = (Flags)f1.clone();
553:         if (f1.equals(fc) && fc.equals(f1))
554:          System.out.println("success");
555:         else
556:          System.out.println("fail");
557:
558:         // add a user flag and make sure it still works right
559:         f1.add("MyFlag");
560:
561:         // shouldn't be equal here
562:         if (!f1.equals(fc) && !fc.equals(f1))
563:          System.out.println("success");
564:         else
565:          System.out.println("fail");
566:
567:         // check clone
568:         fc = (Flags)f1.clone();
569:         if (f1.equals(fc) && fc.equals(f1))
570:          System.out.println("success");
571:         else
572:          System.out.println("fail");
573:
574:         // make sure user flag hash tables are separate
575:         fc.add("AnotherFlag");
576:         if (!f1.equals(fc) && !fc.equals(f1))
577:          System.out.println("success");
578:         else
579:          System.out.println("fail");
580:
581:         // check copy constructor
582:         fc = new Flags(f1);
583:         if (f1.equals(fc) && fc.equals(f1))
584:          System.out.println("success");
585:         else
586:          System.out.println("fail");
587:
588:         // another new flags object
589:         Flags f2 = new Flags(Flags.Flag.ANSWERED);
590:         f2.add("MyFlag");
591:
592:         if (f1.contains(Flags.Flag.DELETED))
593:          System.out.println("success");
594:         else
595:          System.out.println("fail");
596:                 
597:         if (f1.contains(Flags.Flag.SEEN))
598:          System.out.println("success");
599:         else
600:          System.out.println("fail");
601:
602:         if (f1.contains(Flags.Flag.RECENT))
603:          System.out.println("success");
604:         else
605:          System.out.println("fail");
606:
607:         if (f1.contains("MyFlag"))
608:          System.out.println("success");
609:         else
610:          System.out.println("fail");
611:
612:         if (f2.contains(Flags.Flag.ANSWERED))
613:          System.out.println("success");
614:         else
615:          System.out.println("fail");
616:
617:
618:         System.out.println("----------------");
619:
620:         String[] s = f1.getUserFlags();
621:         for (int i = 0; i < s.length; i++)
622:          System.out.println(s[i]);
623:         System.out.println("----------------");
624:         s = f2.getUserFlags();
625:         for (int i = 0; i < s.length; i++)
626:          System.out.println(s[i]);
627:
628:         System.out.println("----------------");
629:
630:         if (f1.contains(f2)) // this should be true
631:          System.out.println("success");
632:         else
633:          System.out.println("fail");
634:
635:         if (!f2.contains(f1)) // this should be false
636:          System.out.println("success");
637:         else
638:          System.out.println("fail");
639:
640:         Flags f3 = new Flags();
641:         f3.add(Flags.Flag.DELETED);
642:         f3.add(Flags.Flag.SEEN);
643:         f3.add(Flags.Flag.RECENT);
644:         f3.add(Flags.Flag.ANSWERED);
645:         f3.add("ANOTHERFLAG");
646:         f3.add("MYFLAG");
647:
648:         f1.add("AnotherFlag");
649:
650:         if (f1.equals(f3))
651:          System.out.println("equals success");
652:         else
653:          System.out.println("fail");
654:         if (f3.equals(f1))
655:          System.out.println("equals success");
656:         else
657:          System.out.println("fail");
658:         System.out.println("f1 hash code " + f1.hashCode());
659:         System.out.println("f3 hash code " + f3.hashCode());
660:         if (f1.hashCode() == f3.hashCode())
661:          System.out.println("success");
662:         else
663:          System.out.println("fail");
664: }
665: ****/
666: }