You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ma...@apache.org on 2017/09/29 07:21:42 UTC

[04/31] james-project git commit: MAILBOX-307 Refactor ACL handling

MAILBOX-307 Refactor ACL handling


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/f388ff94
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/f388ff94
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/f388ff94

Branch: refs/heads/master
Commit: f388ff94128009549b05fcd653199d7dab1b6d7b
Parents: 45c3dfb
Author: benwa <bt...@linagora.com>
Authored: Mon Sep 25 11:35:29 2017 +0700
Committer: Matthieu Baechler <ma...@apache.org>
Committed: Fri Sep 29 09:20:39 2017 +0200

----------------------------------------------------------------------
 mailbox/api/pom.xml                             |    4 +
 .../mailbox/acl/UnionMailboxACLResolver.java    |   10 +-
 .../exception/UnsupportedRightException.java    |    2 +-
 .../apache/james/mailbox/model/MailboxACL.java  |    2 +-
 .../james/mailbox/model/SimpleMailboxACL.java   | 1002 +++++-------------
 .../acl/UnionMailboxACLResolverTest.java        |  541 +++++-----
 .../james/mailbox/model/Rfc4314RightsTest.java  |   72 +-
 .../model/SimpleMailboxACLEntryKeyTest.java     |  160 +--
 .../mailbox/model/SimpleMailboxACLTest.java     |   46 +-
 .../cassandra/mail/CassandraACLMapperTest.java  |   18 +-
 .../json/SimpleMailboxACLJsonConverter.java     |   16 +-
 .../json/SimpleMailboxACLJsonConverterTest.java |   25 +-
 .../imap/processor/DeleteACLProcessor.java      |    9 +-
 .../james/imap/processor/GetACLProcessor.java   |    8 +-
 .../james/imap/processor/GetQuotaProcessor.java |    4 +-
 .../imap/processor/GetQuotaRootProcessor.java   |    4 +-
 .../imap/processor/ListRightsProcessor.java     |   10 +-
 .../james/imap/processor/MyRightsProcessor.java |   14 +-
 .../james/imap/processor/SetACLProcessor.java   |    8 +-
 .../imap/processor/DeleteACLProcessorTest.java  |   13 +-
 .../imap/processor/GetACLProcessorTest.java     |   11 +-
 .../imap/processor/GetQuotaProcessorTest.java   |    6 +-
 .../processor/GetQuotaRootProcessorTest.java    |    7 +-
 .../imap/processor/ListRightsProcessorTest.java |   12 +-
 .../imap/processor/SetACLProcessorTest.java     |   14 +-
 25 files changed, 810 insertions(+), 1208 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/f388ff94/mailbox/api/pom.xml
----------------------------------------------------------------------
diff --git a/mailbox/api/pom.xml b/mailbox/api/pom.xml
index df8c254..d34d3ba 100644
--- a/mailbox/api/pom.xml
+++ b/mailbox/api/pom.xml
@@ -37,6 +37,10 @@
             <artifactId>apache-mime4j-dom</artifactId>
         </dependency>
         <dependency>
+            <groupId>com.github.fge</groupId>
+            <artifactId>throwing-lambdas</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.github.steveash.guavate</groupId>
             <artifactId>guavate</artifactId>
         </dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/f388ff94/mailbox/api/src/main/java/org/apache/james/mailbox/acl/UnionMailboxACLResolver.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/acl/UnionMailboxACLResolver.java b/mailbox/api/src/main/java/org/apache/james/mailbox/acl/UnionMailboxACLResolver.java
index 7ba49ea..545d5df 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/acl/UnionMailboxACLResolver.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/acl/UnionMailboxACLResolver.java
@@ -287,7 +287,7 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
     @Override
     public boolean isReadWrite(MailboxACLRights mailboxACLRights, Flags sharedFlags) throws UnsupportedRightException {
         /* the two fast cases first */
-        if (mailboxACLRights.contains(Rfc4314Rights.i_Insert_RIGHT) || mailboxACLRights.contains(Rfc4314Rights.e_PerformExpunge_RIGHT)) {
+        if (mailboxACLRights.contains(SimpleMailboxACL.Right.Insert) || mailboxACLRights.contains(SimpleMailboxACL.Right.PerformExpunge)) {
             return true;
         }
         /*
@@ -303,12 +303,12 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
          * flags.
          */
         else if (sharedFlags != null) {
-            if (sharedFlags.contains(Flag.DELETED) && mailboxACLRights.contains(Rfc4314Rights.t_DeleteMessages_RIGHT)) {
+            if (sharedFlags.contains(Flag.DELETED) && mailboxACLRights.contains(SimpleMailboxACL.Right.DeleteMessages)) {
                 return true;
-            } else if (sharedFlags.contains(Flag.SEEN) && mailboxACLRights.contains(Rfc4314Rights.s_WriteSeenFlag_RIGHT)) {
+            } else if (sharedFlags.contains(Flag.SEEN) && mailboxACLRights.contains(SimpleMailboxACL.Right.WriteSeenFlag)) {
                 return true;
             } else {
-                boolean hasWriteRight = mailboxACLRights.contains(Rfc4314Rights.w_Write_RIGHT);
+                boolean hasWriteRight = mailboxACLRights.contains(SimpleMailboxACL.Right.Write);
                 return hasWriteRight && (sharedFlags.contains(Flag.ANSWERED) || sharedFlags.contains(Flag.DRAFT) || sharedFlags.contains(Flag.FLAGGED) || sharedFlags.contains(Flag.RECENT) || sharedFlags.contains(Flag.USER));
             }
         }
@@ -377,7 +377,7 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
     }
 
     private static MailboxACLRights[] toListRightsArray(MailboxACLRights implicitRights) throws UnsupportedRightException {
-        List<MailboxACLRights> result = new ArrayList<>(Rfc4314Rights.FIELD_COUNT);
+        List<MailboxACLRights> result = new ArrayList<>();
         result.add(implicitRights);
         for (MailboxACLRight right : SimpleMailboxACL.FULL_RIGHTS) {
             if (!implicitRights.contains(right)) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/f388ff94/mailbox/api/src/main/java/org/apache/james/mailbox/exception/UnsupportedRightException.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/exception/UnsupportedRightException.java b/mailbox/api/src/main/java/org/apache/james/mailbox/exception/UnsupportedRightException.java
index a2cfccd..8ca5dcd 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/exception/UnsupportedRightException.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/exception/UnsupportedRightException.java
@@ -42,7 +42,7 @@ public class UnsupportedRightException extends MailboxSecurityException {
     }
     
     public UnsupportedRightException(MailboxACLRight unsupportedRight) {
-        this(unsupportedRight.getValue());
+        this(unsupportedRight.asCharacter());
     }
 
     public UnsupportedRightException(String msg, Exception cause) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/f388ff94/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java
index 235ddce..b6e97d2 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java
@@ -117,7 +117,7 @@ public interface MailboxACL {
          * 
          * @return char representation of this right
          */
-        char getValue();
+        char asCharacter();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/james-project/blob/f388ff94/mailbox/api/src/main/java/org/apache/james/mailbox/model/SimpleMailboxACL.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/SimpleMailboxACL.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/SimpleMailboxACL.java
index 884e625..2191526 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/SimpleMailboxACL.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/SimpleMailboxACL.java
@@ -19,492 +19,269 @@
 
 package org.apache.james.mailbox.model;
 
-import java.util.Collections;
-import java.util.HashMap;
+import java.util.Arrays;
+import java.util.EnumSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.Properties;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.james.mailbox.exception.UnsupportedRightException;
 
+import com.github.fge.lambdas.Throwing;
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
 /**
  * Default implementation of {@link MailboxACL}.
  * 
  */
 public class SimpleMailboxACL implements MailboxACL {
 
-    /**
-     * Supports only the Standard Rights of RFC 4314 section 2.1. The rights are
-     * stored as single bits in 32 bit int {@link #value} field.
-     */
-    public static class Rfc4314Rights implements MailboxACLRights {
-        /**
-         * See RFC 4314 section 2.1.1. Obsolete Rights.
-         */
-        public enum CompatibilityMode {
-            ck_detx, ckx_det, NO_COMPATIBILITY
-        }
-
-        private class Rfc4314RightsIterator implements Iterator<MailboxACLRight> {
-
-            int position = 0;
-
-            public Rfc4314RightsIterator() {
-                super();
-                nextPostion();
-            }
-
-            @Override
-            public boolean hasNext() {
-                return position < FIELD_COUNT;
-            }
-
-            @Override
-            public MailboxACLRight next() {
-                if (!hasNext()) {
-                    throw new IndexOutOfBoundsException("No next element at position " + position + " from " + FIELD_COUNT + " in " + Rfc4314RightsIterator.class.getName());
-                }
-                MailboxACLRight result = indexRightLookup[position];
-                position++;
-                nextPostion();
-                return result;
-            }
-
-            /**
-             */
-            private void nextPostion() {
-                while (position < FIELD_COUNT && (value & (1 << position)) == 0) {
-                    position++;
-                }
-            }
-
-            @Override
-            public void remove() {
-                throw new java.lang.UnsupportedOperationException("Cannot remove rights through this " + Rfc4314RightsIterator.class.getName());
-            }
-
-        }
-
-        /**
-         * a - administer (perform SETACL/DELETEACL/GETACL/LISTRIGHTS)
-         * 
-         */
-        public static final char a_Administer = 'a';
-
-        static final int a_Administer_MASK = 1;
-        public static final MailboxACLRight a_Administer_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(a_Administer);
-        public static final char c_ObsoleteCreate = 'c';
-        public static final char d_ObsoleteDelete = 'd';
-        /**
-         * e - perform EXPUNGE and expunge as a part of CLOSE
-         * 
-         */
-        public static final char e_PerformExpunge = 'e';
-        static final int e_PerformExpunge_MASK = 1 << 1;
-        public static final MailboxACLRight e_PerformExpunge_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(e_PerformExpunge);
-        public static final int EMPTY_MASK = 0;
-        public static final int FIELD_COUNT = 11;
-        /**
-         * i - insert (perform APPEND, COPY into mailbox)
-         * 
-         */
-        public static final char i_Insert = 'i';
-        static final int i_Insert_MASK = 1 << 2;
-
-        public static final MailboxACLRight i_Insert_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(i_Insert);
-        private static final char[] indexFlagLookup;
-        private static final MailboxACLRight[] indexRightLookup;
-        /**
-         * k - create mailboxes (CREATE new sub-mailboxes in any
-         * implementation-defined hierarchy, parent mailbox for the new mailbox
-         * name in RENAME)
-         * 
-         */
-        public static final char k_CreateMailbox = 'k';
-        static final int k_CreateMailbox_MASK = 1 << 3;
-        public static final MailboxACLRight k_CreateMailbox_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(k_CreateMailbox);
+    public enum Right implements MailboxACLRight {
+        Administer('a'), // (perform SETACL/DELETEACL/GETACL/LISTRIGHTS)
+        PerformExpunge('e'), //perform EXPUNGE and expunge as a part of CLOSE
+        Insert('i'), //insert (perform APPEND, COPY into mailbox)
+        /*
+        * create mailboxes (CREATE new sub-mailboxes in any
+        * implementation-defined hierarchy, parent mailbox for the new mailbox
+        * name in RENAME)
+        * */
+        CreateMailbox('k'),
+        Lookup('l'), //lookup (mailbox is visible to LIST/LSUB commands, SUBSCRIBE mailbox)
+        Post('p'), //post (send mail to submission address for mailbox, not enforced by IMAP4 itself)
+        Read('r'), //read (SELECT the mailbox, perform STATUS)
         /**
-         * l - lookup (mailbox is visible to LIST/LSUB commands, SUBSCRIBE
-         * mailbox)
-         * 
+         * keep seen/unseen information across sessions (set or clear \SEEN
+         * flag via STORE, also set \SEEN during APPEND/COPY/ FETCH BODY[...])
          */
-        public static final char l_Lookup = 'l';
-        static final int l_Lookup_MASK = 1 << 4;
-        public static final MailboxACLRight l_Lookup_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(l_Lookup);
+        WriteSeenFlag('s'),
+        DeleteMessages('t'), //delete messages (set or clear \DELETED flag via STORE, set \DELETED flag during APPEND/COPY)
         /**
-         * p - post (send mail to submission address for mailbox, not enforced
-         * by IMAP4 itself)
-         * 
+         * write (set or clear flags other than \SEEN and \DELETED via
+         * STORE, also set them during APPEND/COPY)
          */
-        public static final char p_Post = 'p';
-        static final int p_Post_MASK = 1 << 5;
-        public static final MailboxACLRight p_Post_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(p_Post);
+        Write('w'),
+        DeleteMailbox('x'); //delete mailbox (DELETE mailbox, old mailbox name in RENAME)
 
-        /**
-         * r - read (SELECT the mailbox, perform STATUS)
-         * 
-         */
-        public static final char r_Read = 'r';
-        static final int r_Read_MASK = 1 << 6;
+        private final char rightCharacter;
 
-        public static final MailboxACLRight r_Read_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(r_Read);
-        /**
-         * s - keep seen/unseen information across sessions (set or clear \SEEN
-         * flag via STORE, also set \SEEN during APPEND/COPY/ FETCH BODY[...])
-         * 
-         */
-        public static final char s_WriteSeenFlag = 's';
+        Right(char rightCharacter) {
+            this.rightCharacter = rightCharacter;
+        }
 
-        static final int s_WriteSeenFlag_MASK = 1 << 7;
+        @Override
+        public char asCharacter() {
+            return rightCharacter;
+        }
 
-        public static final MailboxACLRight s_WriteSeenFlag_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(s_WriteSeenFlag);
+        public static final EnumSet<Right> allRights = EnumSet.allOf(Right.class);
 
-        public static final char t_DeleteMessages = 't';
+        public static Right forChar(char c) throws UnsupportedRightException {
+            return Right.allRights
+                .stream()
+                .filter(r -> r.asCharacter() == c)
+                .findFirst()
+                .orElseThrow(() -> new UnsupportedRightException(c));
+        }
+    }
 
+    /**
+     * Supports only the Standard Rights of RFC 4314 section 2.1. The rights are
+     * stored as single bits in 32 bit int {@link #value} field.
+     */
+    public static class Rfc4314Rights implements MailboxACLRights {
         /**
-         * t - delete messages (set or clear \DELETED flag via STORE, set
-         * \DELETED flag during APPEND/COPY)
-         * 
-         */
-        static final int t_DeleteMessages_MASK = 1 << 8;
-        public static final MailboxACLRight t_DeleteMessages_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(t_DeleteMessages);
-        /**
-         * w - write (set or clear flags other than \SEEN and \DELETED via
-         * STORE, also set them during APPEND/COPY)
-         * 
-         */
-        public static final char w_Write = 'w';
-        static final int w_Write_MASK = 1 << 9;
-        public static final MailboxACLRight w_Write_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(w_Write);
-        /**
-         * x - delete mailbox (DELETE mailbox, old mailbox name in RENAME)
-         * 
+         * See RFC 4314 section 2.1.1. Obsolete Rights.
          */
-        public static final char x_DeleteMailbox = 'x';
-        static final int x_DeleteMailbox_MASK = 1 << 10;
-        public static final MailboxACLRight x_DeleteMailbox_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(x_DeleteMailbox);
-        static {
-            indexFlagLookup = new char[] { a_Administer, e_PerformExpunge, i_Insert, k_CreateMailbox, l_Lookup, p_Post, r_Read, s_WriteSeenFlag, t_DeleteMessages, w_Write, x_DeleteMailbox };
-            indexRightLookup = new MailboxACLRight[] { a_Administer_RIGHT, e_PerformExpunge_RIGHT, i_Insert_RIGHT, k_CreateMailbox_RIGHT, l_Lookup_RIGHT, p_Post_RIGHT, r_Read_RIGHT, s_WriteSeenFlag_RIGHT, t_DeleteMessages_RIGHT, w_Write_RIGHT, x_DeleteMailbox_RIGHT };
+        public enum CompatibilityMode {
+            ck_detx, ckx_det, NO_COMPATIBILITY
         }
 
-        private static int flagMaskLookup(char flag) throws UnsupportedRightException {
-            switch (flag) {
-            case a_Administer:
-                return a_Administer_MASK;
-            case e_PerformExpunge:
-                return e_PerformExpunge_MASK;
-            case i_Insert:
-                return i_Insert_MASK;
-            case k_CreateMailbox:
-                return k_CreateMailbox_MASK;
-            case l_Lookup:
-                return l_Lookup_MASK;
-            case p_Post:
-                return p_Post_MASK;
-            case r_Read:
-                return r_Read_MASK;
-            case s_WriteSeenFlag:
-                return s_WriteSeenFlag_MASK;
-            case t_DeleteMessages:
-                return t_DeleteMessages_MASK;
-            case w_Write:
-                return w_Write_MASK;
-            case x_DeleteMailbox:
-                return x_DeleteMailbox_MASK;
-            default:
-                throw new UnsupportedRightException(flag);
-            }
-        }
+        private static final char c_ObsoleteCreate = 'c';
+        private static final char d_ObsoleteDelete = 'd';
 
         /**
          * See RFC 4314 section 2.1.1. Obsolete Rights.
          */
         private final CompatibilityMode compatibilityMode = CompatibilityMode.ckx_det;
 
-        /**
-         * 32 bit <code>int</code> to store the rights.
-         */
-        private final int value;
+        private final EnumSet<Right> value;
 
-        private Rfc4314Rights() {
-            this.value = EMPTY_MASK;
+        private Rfc4314Rights(EnumSet<Right> rights) {
+            this.value = EnumSet.copyOf(rights);
         }
 
-        public Rfc4314Rights(boolean canAdminister, boolean canCreateMailbox, boolean canDeleteMailbox, boolean canDeleteMessages, boolean canInsert, boolean canLookup, boolean canPerformExpunge, boolean canPost, boolean canRead, boolean canWrite, boolean canWriteSeenFlag) {
-            super();
-            int v = 0;
-
-            if (canAdminister) {
-                v |= a_Administer_MASK;
-            }
-            if (canCreateMailbox) {
-                v |= k_CreateMailbox_MASK;
-            }
-            if (canDeleteMailbox) {
-                v |= x_DeleteMailbox_MASK;
-            }
-            if (canDeleteMessages) {
-                v |= t_DeleteMessages_MASK;
-            }
-            if (canInsert) {
-                v |= i_Insert_MASK;
-            }
-            if (canLookup) {
-                v |= l_Lookup_MASK;
-            }
-            if (canPerformExpunge) {
-                v |= e_PerformExpunge_MASK;
-            }
-            if (canPost) {
-                v |= p_Post_MASK;
-            }
-            if (canRead) {
-                v |= r_Read_MASK;
-            }
-            if (canWrite) {
-                v |= w_Write_MASK;
-            }
-            if (canWriteSeenFlag) {
-                v |= s_WriteSeenFlag_MASK;
-            }
+        private Rfc4314Rights() {
+            this(EnumSet.noneOf(Right.class));
+        }
 
-            this.value = v;
+        public Rfc4314Rights(Right... rights) {
+            this(EnumSet.copyOf(Arrays.asList(rights)));
+        }
 
+        public Rfc4314Rights(MailboxACLRight right) throws UnsupportedRightException {
+            this.value = EnumSet.of(Right.forChar(right.asCharacter()));
         }
 
-        public Rfc4314Rights(int value) throws UnsupportedRightException {
-            if ((value >> FIELD_COUNT) != 0) {
-                throw new UnsupportedRightException();
+        /* Used for json serialization (probably a bad idea) */
+        public Rfc4314Rights(int serializedRights) {
+            List<Right> rights = Right.allRights.stream()
+                .filter(right -> ((serializedRights >> right.ordinal()) & 1) != 0)
+                .collect(Collectors.toList());
+            if (rights.isEmpty()) {
+                this.value = EnumSet.noneOf(Right.class);
+            } else {
+                this.value = EnumSet.copyOf(rights);
             }
-            this.value = value;
-        }
-        
-        public Rfc4314Rights(MailboxACLRight right) throws UnsupportedRightException {
-            this.value = flagMaskLookup(right.getValue());
         }
 
         public Rfc4314Rights(String serializedRfc4314Rights) throws UnsupportedRightException {
-            int v = 0;
-
-            for (int i = 0; i < serializedRfc4314Rights.length(); i++) {
-                char flag = serializedRfc4314Rights.charAt(i);
-                switch (flag) {
-                case c_ObsoleteCreate:
-                    switch (compatibilityMode) {
-                    case ck_detx:
-                        v |= k_CreateMailbox_MASK;
-                        break;
-                    case ckx_det:
-                        v |= k_CreateMailbox_MASK;
-                        v |= x_DeleteMailbox_MASK;
-                        break;
-                    case NO_COMPATIBILITY:
-                        throw new UnsupportedRightException(flag);
-                    default:
-                        throw new IllegalStateException("Unexpected enum member: " + CompatibilityMode.class.getName() + "." + compatibilityMode.name());
-                    }
-                    break;
-                case d_ObsoleteDelete:
-                    switch (compatibilityMode) {
-                    case ck_detx:
-                        v |= e_PerformExpunge_MASK;
-                        v |= t_DeleteMessages_MASK;
-                        v |= x_DeleteMailbox_MASK;
-                        break;
-                    case ckx_det:
-                        v |= e_PerformExpunge_MASK;
-                        v |= t_DeleteMessages_MASK;
-                        break;
-                    case NO_COMPATIBILITY:
-                        throw new UnsupportedRightException(flag);
-                    default:
-                        throw new IllegalStateException("Unexpected enum member: " + CompatibilityMode.class.getName() + "." + compatibilityMode.name());
-                    }
-                    break;
-                default:
-                    v |= flagMaskLookup(flag);
-                }
+            List<Right> rights = serializedRfc4314Rights.chars()
+                .mapToObj(i -> (char) i)
+                .flatMap(Throwing.function(this::convert).sneakyThrow())
+                .collect(Collectors.toList());
+            if (rights.isEmpty()) {
+                this.value = EnumSet.noneOf(Right.class);
+            } else {
+                this.value = EnumSet.copyOf(rights);
             }
-            this.value = v;
-
         }
 
-        public boolean contains(char flag) throws UnsupportedRightException {
-
+        private Stream<Right> convert(char flag) throws UnsupportedRightException {
             switch (flag) {
             case c_ObsoleteCreate:
-                switch (compatibilityMode) {
-                case ck_detx:
-                    return (value & k_CreateMailbox_MASK) != 0;
-                case ckx_det:
-                    return (value & (k_CreateMailbox_MASK | x_DeleteMailbox_MASK)) != 0;
-                case NO_COMPATIBILITY:
-                    throw new UnsupportedRightException(flag);
-                default:
-                    throw new IllegalStateException("Unexpected enum member: " + CompatibilityMode.class.getName() + "." + compatibilityMode.name());
-                }
+                return convertObsoleteCreate(flag);
             case d_ObsoleteDelete:
-                switch (compatibilityMode) {
-                case ck_detx:
-                    return (value & (e_PerformExpunge_MASK | t_DeleteMessages_MASK | x_DeleteMailbox_MASK)) != 0;
-                case ckx_det:
-                    return (value & (e_PerformExpunge_MASK | t_DeleteMessages_MASK)) != 0;
-                case NO_COMPATIBILITY:
-                    throw new UnsupportedRightException(flag);
-                default:
-                    throw new IllegalStateException("Unexpected enum member: " + CompatibilityMode.class.getName() + "." + compatibilityMode.name());
-                }
+                return convertObsoleteDelete(flag);
             default:
-                return (value & flagMaskLookup(flag)) != 0;
+                return Stream.of(Right.forChar(flag));
             }
         }
 
-        /** 
-         * @see
-         * org.apache.james.mailbox.MailboxACL.MailboxACLRights#contains(org
-         * .apache.james.mailbox.MailboxACL.MailboxACLRight)
-         */
+        private Stream<Right> convertObsoleteDelete(char flag) throws UnsupportedRightException {
+            switch (compatibilityMode) {
+            case ck_detx:
+                return Stream.of(Right.PerformExpunge, Right.DeleteMessages, Right.DeleteMailbox);
+            case ckx_det:
+                return Stream.of(Right.PerformExpunge, Right.DeleteMessages);
+            case NO_COMPATIBILITY:
+                throw new UnsupportedRightException(flag);
+            default:
+                throw new IllegalStateException("Unexpected enum member: " + CompatibilityMode.class.getName() + "." + compatibilityMode.name());
+            }
+        }
+
+        private Stream<Right> convertObsoleteCreate(char flag) throws UnsupportedRightException {
+            switch (compatibilityMode) {
+            case ck_detx:
+                return Stream.of(Right.CreateMailbox);
+            case ckx_det:
+                return Stream.of(Right.CreateMailbox, Right.DeleteMailbox);
+            case NO_COMPATIBILITY:
+                throw new UnsupportedRightException(flag);
+            default:
+                throw new IllegalStateException("Unexpected enum member: " + CompatibilityMode.class.getName() + "." + compatibilityMode.name());
+            }
+        }
+
+        public boolean contains(char flag) throws UnsupportedRightException {
+            return contains(Right.forChar(flag));
+        }
+
         @Override
         public boolean contains(MailboxACLRight right) throws UnsupportedRightException {
-            return contains(right.getValue());
+            return value.contains(Right.forChar(right.asCharacter()));
+        }
+
+        /* Used for json serialization (probably a bad idea) */
+        public int serializeAsInteger() {
+            return value.stream().mapToInt(x -> 1 << x.ordinal()).sum();
         }
 
         @Override
         public boolean equals(Object o) {
             if (o instanceof Rfc4314Rights) {
-                return this.value == ((Rfc4314Rights) o).value;
+                Rfc4314Rights that = (Rfc4314Rights) o;
+                return this.value.equals(that.value);
             } else if (o instanceof MailboxACLRights) {
                 try {
-                    return this.value == new Rfc4314Rights(((MailboxACLRights) o).serialize()).value;
+                    MailboxACLRights that = (MailboxACLRights) o;
+                    return this.value == new Rfc4314Rights(that.serialize()).value;
                 } catch (UnsupportedRightException e) {
                     throw new RuntimeException(e);
                 }
-            } else {
-                return false;
             }
+            return false;
         }
 
-        /** 
-         * @see
-         * org.apache.james.mailbox.MailboxACL.MailboxACLRights#except(org.apache
-         * .james.mailbox.MailboxACL.MailboxACLRights)
-         */
         @Override
         public MailboxACLRights except(MailboxACLRights toRemove) throws UnsupportedRightException {
-            if (this.value == EMPTY_MASK || toRemove == null || toRemove.isEmpty()) {
-                /* nothing to remove */
-                return this;
-            } else if (toRemove instanceof Rfc4314Rights) {
-                Rfc4314Rights other = (Rfc4314Rights) toRemove;
-                if (other.value == EMPTY_MASK) {
-                    /* toRemove is an identity element */
-                    return this;
-                } else {
-                    return new Rfc4314Rights(this.value & (~((other).value)));
-                }
-            } else {
-                return new Rfc4314Rights(this.value & (~(new Rfc4314Rights(toRemove.serialize()).value)));
-            }
+            EnumSet<Right> copy = EnumSet.copyOf(value);
+            copy.removeAll(convertRightsToList(toRemove));
+            return new Rfc4314Rights(copy);
         }
 
-        public int getValue() {
-            return value;
-        }
-
-        /**
-         * Returns {@link #value}.
-         * 
-         * @see java.lang.Object#hashCode()
-         */
-        @Override
-        public int hashCode() {
-            return value;
-        }
-
-        /**
-         * @see org.apache.james.mailbox.model.MailboxACL.MailboxACLRights#isEmpty()
-         */
         @Override
         public boolean isEmpty() {
-            return value == EMPTY_MASK;
+            return value.isEmpty();
         }
 
-        /** 
-         * @see
-         * org.apache.james.mailbox.MailboxACL.MailboxACLRights#isSupported(
-         * org.apache.james.mailbox.MailboxACL.MailboxACLRight)
-         */
         @Override
         public boolean isSupported(MailboxACLRight right) {
             try {
-                contains(right.getValue());
+                contains(right.asCharacter());
                 return true;
             } catch (UnsupportedRightException e) {
                 return false;
             }
         }
 
-        /*
-         * (non-Javadoc)
-         * 
-         * @see java.lang.Iterable#iterator()
-         */
         @Override
         public Iterator<MailboxACLRight> iterator() {
-            return new Rfc4314RightsIterator();
+            ImmutableList<MailboxACLRight> rights = ImmutableList.copyOf(value);
+            return rights.iterator();
         }
 
-        /*
-         * (non-Javadoc)
-         * 
-         * @see org.apache.james.mailbox.MailboxACL.MailboxACLRights#serialize()
-         */
         @Override
         public String serialize() {
-            StringBuilder result = new StringBuilder(FIELD_COUNT);
-            for (int i = 0; i < FIELD_COUNT; i++) {
-                if ((value & (1 << i)) != 0) {
-                    result.append(indexFlagLookup[i]);
-                }
-            }
-            return result.toString();
+            return value.stream()
+                .map(Right::asCharacter)
+                .map(String::valueOf)
+                .collect(Collectors.joining());
         }
 
-        /**
-         * Returns {@link #serialize()}
-         * 
-         * @see java.lang.Object#toString()
-         */
         @Override
         public String toString() {
             return serialize();
         }
 
-        /*
-         * (non-Javadoc)
-         * 
-         * @see
-         * org.apache.james.mailbox.MailboxACL.MailboxACLRights#union(org.apache
-         * .james.mailbox.MailboxACL.MailboxACLRights)
-         */
         @Override
         public MailboxACLRights union(MailboxACLRights toAdd) throws UnsupportedRightException {
-            if (this.value == EMPTY_MASK) {
-                /* this is an identity element */
-                return toAdd;
-            } else if (toAdd instanceof Rfc4314Rights) {
-                Rfc4314Rights other = (Rfc4314Rights) toAdd;
-                if (other.value == EMPTY_MASK) {
-                    /* toAdd is an identity element */
-                    return this;
-                } else {
-                    return new Rfc4314Rights(this.value | other.value);
-                }
-            } else {
-                return new Rfc4314Rights(this.value | new Rfc4314Rights(toAdd.serialize()).value);
-            }
+            Preconditions.checkNotNull(toAdd);
+            EnumSet<Right> rightUnion = EnumSet.noneOf(Right.class);
+            rightUnion.addAll(value);
+            rightUnion.addAll(convertRightsToList(toAdd));
+            return new Rfc4314Rights(rightUnion);
+        }
+
+        private List<Right> convertRightsToList(MailboxACLRights toAdd) {
+            return ImmutableList.copyOf(Optional.ofNullable(toAdd).orElse(Rfc4314Rights.empty()))
+                .stream()
+                .map(Throwing.function(right -> Right.forChar(right.asCharacter())))
+                .collect(Guavate.toImmutableList());
+        }
+
+        private static MailboxACLRights empty() {
+            return new Rfc4314Rights();
         }
 
     }
@@ -524,24 +301,14 @@ public class SimpleMailboxACL implements MailboxACL {
             this.value = value;
         }
         public SimpleMailboxACLEntry(String key, String value) throws UnsupportedRightException {
-            this(new SimpleMailboxACLEntryKey(key), new Rfc4314Rights(value));
+            this(SimpleMailboxACLEntryKey.deserialize(key), new Rfc4314Rights(value));
         }
 
-        /*
-         * (non-Javadoc)
-         * 
-         * @see java.util.Map.Entry#getKey()
-         */
         @Override
         public MailboxACLEntryKey getKey() {
             return key;
         }
 
-        /*
-         * (non-Javadoc)
-         * 
-         * @see java.util.Map.Entry#getValue()
-         */
         @Override
         public MailboxACLRights getValue() {
             return value;
@@ -554,7 +321,7 @@ public class SimpleMailboxACL implements MailboxACL {
          */
         @Override
         public MailboxACLRights setValue(MailboxACLRights value) {
-            throw new java.lang.UnsupportedOperationException("Fields of " + MailboxACLRights.class.getName() + " are read only.");
+            throw new UnsupportedOperationException("Fields of " + MailboxACLRights.class.getName() + " are read only.");
         }
 
     }
@@ -579,7 +346,6 @@ public class SimpleMailboxACL implements MailboxACL {
             return new SimpleMailboxACLEntryKey(name, NameType.user, negative);
         }
 
-        private final int hash;
         private final String name;
         private final NameType nameType;
         private final boolean negative;
@@ -592,109 +358,67 @@ public class SimpleMailboxACL implements MailboxACL {
          * 
          * @param serialized
          */
-        public SimpleMailboxACLEntryKey(String serialized) {
+        public static SimpleMailboxACLEntryKey deserialize(String serialized) {
+            Preconditions.checkNotNull(serialized, "Cannot parse null");
+            Preconditions.checkArgument(!serialized.isEmpty(), "Cannot parse an empty string");
 
-            if (serialized == null) {
-                throw new IllegalStateException("Cannot parse null to a " + getClass().getName());
-            }
-            if (serialized.length() == 0) {
-                throw new IllegalStateException("Cannot parse an empty string to a " + getClass().getName());
-            }
-            int start = 0;
-            if (serialized.charAt(start) == DEFAULT_NEGATIVE_MARKER) {
-                negative = true;
-                start++;
-            } else {
-                negative = false;
-            }
-            if (serialized.charAt(start) == DEFAULT_GROUP_MARKER) {
-                nameType = NameType.group;
-                start++;
-                name = serialized.substring(start);
-                if (name.length() == 0) {
-                    throw new IllegalStateException("Cannot parse a string with empty name to a " + getClass().getName());
-                }
-            } else {
-                name = serialized.substring(start);
-                if (name.length() == 0) {
-                    throw new IllegalStateException("Cannot parse a string with empty name to a " + getClass().getName());
-                }
-                NameType nt = NameType.user;
-                for (SpecialName specialName : SpecialName.values()) {
-                    if (specialName.name().equals(name)) {
-                        nt = NameType.special;
-                        break;
-                    }
-                }
-                this.nameType = nt;
+            boolean negative = serialized.charAt(0) == DEFAULT_NEGATIVE_MARKER;
+            int nameStart = negative ? 1 : 0;
+            boolean isGroup = serialized.charAt(nameStart) == DEFAULT_GROUP_MARKER;
+            Optional<NameType> explicitNameType = isGroup ? Optional.of(NameType.group) : Optional.empty();
+            String name = isGroup ? serialized.substring(nameStart + 1) : serialized.substring(nameStart);
+
+            if (name.isEmpty()) {
+                throw new IllegalStateException("Cannot parse a string with empty name");
             }
+            NameType nameType = explicitNameType.orElseGet(() -> computeImplicitNameType(name));
 
-            this.hash = hash();
+            return new SimpleMailboxACLEntryKey(name, nameType, negative);
+        }
 
+        private static NameType computeImplicitNameType(String name) {
+            boolean isSpecialName = Arrays.stream(SpecialName.values())
+                .anyMatch(specialName -> specialName.name().equals(name));
+            if (isSpecialName) {
+                return NameType.special;
+            }
+            return NameType.user;
         }
 
         public SimpleMailboxACLEntryKey(String name, NameType nameType, boolean negative) {
-            super();
-            if (name == null) {
-                throw new NullPointerException("Provide a name for this " + getClass().getName());
-            }
-            if (nameType == null) {
-                throw new NullPointerException("Provide a nameType for this " + getClass().getName());
-            }
+            Preconditions.checkNotNull(name, "Provide a name for this " + getClass().getName());
+            Preconditions.checkNotNull(nameType, "Provide a nameType for this " + getClass().getName());
+
             this.name = name;
             this.nameType = nameType;
             this.negative = negative;
-            this.hash = hash();
         }
 
         @Override
         public boolean equals(Object o) {
             if (o instanceof MailboxACLEntryKey) {
                 MailboxACLEntryKey other = (MailboxACLEntryKey) o;
-                return this.name.equals(other.getName()) && this.nameType.equals(other.getNameType()) && this.negative == other.isNegative();
-            } else {
-                return false;
+                return Objects.equals(this.name, other.getName())
+                    && Objects.equals(this.nameType, other.getNameType())
+                    && Objects.equals(this.negative, other.isNegative());
             }
+            return false;
         }
 
-        /*
-         * (non-Javadoc)
-         * 
-         * @see org.apache.james.mailbox.MailboxACL.MailboxACLEntryKey#getName()
-         */
+
         public String getName() {
             return name;
         }
 
-        /*
-         * (non-Javadoc)
-         * 
-         * @see
-         * org.apache.james.mailbox.MailboxACL.MailboxACLEntryKey#getNameType()
-         */
         public NameType getNameType() {
             return nameType;
         }
 
-        private int hash() {
-            final int PRIME = 31;
-            int hash = negative ? 1 : 0;
-            hash = PRIME * hash + nameType.hashCode();
-            hash = PRIME * hash + name.hashCode();
-            return hash;
-        }
-
         @Override
-        public int hashCode() {
-            return hash;
+        public final int hashCode() {
+            return Objects.hash(negative, nameType, name);
         }
 
-        /*
-         * (non-Javadoc)
-         * 
-         * @see
-         * org.apache.james.mailbox.MailboxACL.MailboxACLEntryKey#isNegative()
-         */
         public boolean isNegative() {
             return negative;
         }
@@ -708,94 +432,18 @@ public class SimpleMailboxACL implements MailboxACL {
          */
         @Override
         public String serialize() {
-            if (!negative) {
-                switch (nameType) {
-                case special:
-                case user:
-                    return name;
-                case group:
-                    return new StringBuilder(name.length() + 1).append(DEFAULT_GROUP_MARKER).append(name).toString();
-                default:
-                    throw new IllegalStateException();
-                }
-            } else {
-                StringBuilder result = new StringBuilder(name.length() + 2).append(DEFAULT_NEGATIVE_MARKER);
-                switch (nameType) {
-                case special:
-                case user:
-                    break;
-                case group:
-                    result.append(DEFAULT_GROUP_MARKER);
-                    break;
-                default:
-                    throw new IllegalStateException();
-                }
-                return result.append(name).toString();
-            }
+            String negativePart = negative ? String.valueOf(DEFAULT_NEGATIVE_MARKER) : "";
+            String nameTypePart = nameType == NameType.group ? String.valueOf(DEFAULT_GROUP_MARKER) : "";
+
+            return negativePart + nameTypePart + name;
         }
 
         @Override
         public String toString() {
             return serialize();
         }
-
     }
 
-    /**
-     * Default implementation of {@link MailboxACLRight}.
-     */
-    public static final class SimpleMailboxACLRight implements MailboxACLRight {
-        private final char value;
-
-        public SimpleMailboxACLRight(char value) {
-            super();
-            this.value = value;
-        }
-
-        /*
-         * (non-Javadoc)
-         * 
-         * @see java.lang.Object#equals(java.lang.Object)
-         */
-        @Override
-        public boolean equals(Object o) {
-            if (o instanceof MailboxACLRight) {
-                return ((MailboxACLRight) o).getValue() == this.value;
-            }
-            return false;
-        }
-
-        /*
-         * (non-Javadoc)
-         * 
-         * @see org.apache.james.mailbox.MailboxACL.MailboxACLRight#getValue()
-         */
-        @Override
-        public char getValue() {
-            return value;
-        }
-
-        /*
-         * (non-Javadoc)
-         * 
-         * @see java.lang.Object#hashCode()
-         */
-        @Override
-        public int hashCode() {
-            return (int) value;
-        }
-
-        /**
-         * Returns <code>String.valueOf(value)</code>.
-         * 
-         * @see java.lang.Object#toString()
-         */
-        @Override
-        public String toString() {
-            return String.valueOf(value);
-        }
-
-    }
 
     public static class SimpleMailboxACLCommand implements MailboxACLCommand {
         private final MailboxACLEntryKey key;
@@ -824,24 +472,20 @@ public class SimpleMailboxACL implements MailboxACL {
         }
 
         @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof SimpleMailboxACLCommand)) return false;
-
-            SimpleMailboxACLCommand that = (SimpleMailboxACLCommand) o;
-
-            if (key != null ? !key.equals(that.key) : that.key != null) return false;
-            if (editMode != that.editMode) return false;
-            return !(rights != null ? !rights.equals(that.rights) : that.rights != null);
+        public final boolean equals(Object o) {
+            if (o instanceof SimpleMailboxACLCommand) {
+                SimpleMailboxACLCommand that = (SimpleMailboxACLCommand) o;
 
+                return Objects.equals(this.key, that.key)
+                    && Objects.equals(this.editMode, that.editMode)
+                    && Objects.equals(this.rights, that.rights);
+            }
+            return false;
         }
 
         @Override
-        public int hashCode() {
-            int result = key != null ? key.hashCode() : 0;
-            result = 31 * result + (editMode != null ? editMode.hashCode() : 0);
-            result = 31 * result + (rights != null ? rights.hashCode() : 0);
-            return result;
+        public final int hashCode() {
+            return Objects.hash(key, editMode, rights);
         }
     }
 
@@ -867,16 +511,24 @@ public class SimpleMailboxACL implements MailboxACL {
             AUTHENTICATED_KEY = new SimpleMailboxACLEntryKey(SpecialName.authenticated.name(), NameType.special, false);
             AUTHENTICATED_NEGATIVE_KEY = new SimpleMailboxACLEntryKey(SpecialName.authenticated.name(), NameType.special, true);
             EMPTY = new SimpleMailboxACL();
-            FULL_RIGHTS =  new Rfc4314Rights(true, true, true, true, true, true, true, true, true, true, true);
+            FULL_RIGHTS =  new Rfc4314Rights(Right.allRights);
             NO_RIGHTS = new Rfc4314Rights();
             OWNER_KEY = new SimpleMailboxACLEntryKey(SpecialName.owner.name(), NameType.special, false);
             OWNER_NEGATIVE_KEY = new SimpleMailboxACLEntryKey(SpecialName.owner.name(), NameType.special, true);
             OWNER_FULL_ACL = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACL.OWNER_KEY, SimpleMailboxACL.FULL_RIGHTS) });
-            OWNER_FULL_EXCEPT_ADMINISTRATION_ACL = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACL.OWNER_KEY, SimpleMailboxACL.FULL_RIGHTS.except(new Rfc4314Rights(Rfc4314Rights.a_Administer_MASK))) });
+            OWNER_FULL_EXCEPT_ADMINISTRATION_ACL = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACL.OWNER_KEY, SimpleMailboxACL.FULL_RIGHTS.except(new Rfc4314Rights(Right.Administer))) });
         } catch (UnsupportedRightException e) {
             throw new RuntimeException(e);
         }
     }
+
+    private static Map<MailboxACLEntryKey, MailboxACLRights> toMap(Properties props) throws UnsupportedRightException {
+        ImmutableMap.Builder<MailboxACLEntryKey, MailboxACLRights> builder = ImmutableMap.builder();
+        for (Entry<Object, Object> prop : props.entrySet()) {
+            builder.put(SimpleMailboxACLEntryKey.deserialize((String) prop.getKey()), new Rfc4314Rights((String) prop.getValue()));
+        }
+        return builder.build();
+    }
     
     private final Map<MailboxACLEntryKey, MailboxACLRights> entries;
 
@@ -885,7 +537,7 @@ public class SimpleMailboxACL implements MailboxACL {
      * 
      */
     public SimpleMailboxACL() {
-        this.entries = Collections.emptyMap();
+        this(ImmutableMap.of());
     }
 
     /**
@@ -894,47 +546,24 @@ public class SimpleMailboxACL implements MailboxACL {
      * 
      * @param entries
      */
-    public SimpleMailboxACL(Map.Entry<MailboxACLEntryKey, MailboxACLRights>[] entries) {
-        if (entries != null) {
-            Map<MailboxACLEntryKey, MailboxACLRights> m = new HashMap<>(entries.length + entries.length / 2 + 1);
-            for (Entry<MailboxACLEntryKey, MailboxACLRights> en : entries) {
-                m.put(en.getKey(), en.getValue());
-            }
-            this.entries = Collections.unmodifiableMap(m);
-        } else {
-            this.entries = Collections.emptyMap();
-        }
+    public SimpleMailboxACL(Map.Entry<MailboxACLEntryKey, MailboxACLRights>... entries) {
+        this(ImmutableMap.copyOf(
+            Optional.ofNullable(entries)
+                .map(array -> Arrays.stream(array)
+                    .collect(Guavate.toImmutableMap(Entry::getKey, Entry::getValue)))
+            .orElse(ImmutableMap.of())));
     }
 
     /**
      * Creates a new instance of SimpleMailboxACL from the given {@link Map} of
      * entries.
-     * 
+     *
      * @param entries
      */
     public SimpleMailboxACL(Map<MailboxACLEntryKey, MailboxACLRights> entries) {
-        if (entries != null && entries.size() > 0) {
-            Map<MailboxACLEntryKey, MailboxACLRights> m = new HashMap<>(entries.size() + entries.size() / 2 + 1);
-            for (Entry<MailboxACLEntryKey, MailboxACLRights> en : entries.entrySet()) {
-                m.put(en.getKey(), en.getValue());
-            }
-            this.entries = Collections.unmodifiableMap(m);
-        } else {
-            this.entries = Collections.emptyMap();
-        }
-    }
+        Preconditions.checkNotNull(entries);
 
-    /**
-     * Creates a new instance of SimpleMailboxACL.
-     * <code>unmodifiableEntries</code> parameter is supposed to be umodifiable
-     * already.
-     * 
-     * @param unmodifiableEntries
-     * @param dummy
-     *            just to be different from {@link #SimpleMailboxACL(Map)}.
-     */
-    private SimpleMailboxACL(Map<MailboxACLEntryKey, MailboxACLRights> unmodifiableEntries, boolean dummy) {
-        this.entries = unmodifiableEntries;
+        this.entries = ImmutableMap.copyOf(entries);
     }
 
     /**
@@ -947,33 +576,24 @@ public class SimpleMailboxACL implements MailboxACL {
      * @throws UnsupportedRightException
      */
     public SimpleMailboxACL(Properties props) throws UnsupportedRightException {
-        super();
-
-        Map<MailboxACLEntryKey, MailboxACLRights> m = new HashMap<>(props.size() + props.size() / 2 + 1);
-
-        if (props != null) {
-            for (Map.Entry<Object, Object> prop : props.entrySet()) {
-                m.put(new SimpleMailboxACLEntryKey((String) prop.getKey()), new Rfc4314Rights((String) prop.getValue()));
-            }
-        }
-
-        entries = Collections.unmodifiableMap(m);
+        this(toMap(props));
     }
 
-    /**
-     * @see java.lang.Object#equals(java.lang.Object)
-     */
     @Override
     public boolean equals(Object o) {
         if (o instanceof MailboxACL) {
             MailboxACL acl = (MailboxACL) o;
-            Map<MailboxACLEntryKey, MailboxACLRights> ens = acl.getEntries();
-            return entries == ens || (entries != null && entries.equals(ens));
+            return Objects.equals(this.getEntries(), acl.getEntries());
         }
         return false;
     }
 
     @Override
+    public int hashCode() {
+        return Objects.hash(entries);
+    }
+
+    @Override
     public MailboxACL apply(MailboxACLCommand aclUpdate) throws UnsupportedRightException {
         switch (aclUpdate.getEditMode()) {
             case ADD:
@@ -986,144 +606,74 @@ public class SimpleMailboxACL implements MailboxACL {
         throw new RuntimeException("Unknown edit mode");
     }
 
-    /**
-     * @see org.apache.james.mailbox.MailboxACL#except(org.apache.james.mailbox.MailboxACL)
-     */
     @Override
     public MailboxACL except(MailboxACL other) throws UnsupportedRightException {
-        if (entries.size() == 0) {
-            return this;
-        } else {
-            Map<MailboxACLEntryKey, MailboxACLRights> otherEntries = other.getEntries();
-            Map<MailboxACLEntryKey, MailboxACLRights> resultEntries = new HashMap<>(this.entries);
-            for (Entry<MailboxACLEntryKey, MailboxACLRights> otherEntry : otherEntries.entrySet()) {
-                MailboxACLEntryKey key = otherEntry.getKey();
-                MailboxACLRights thisRights = resultEntries.get(key);
-                if (thisRights == null) {
-                    /* nothing to diff */
-                } else {
-                    /* diff */
-                    MailboxACLRights resultRights = thisRights.except(otherEntry.getValue());
-                    if (!resultRights.isEmpty()) {
-                        resultEntries.put(key, resultRights);
-                    }
-                    else {
-                        resultEntries.remove(key);
-                    }
-                }
-            }
-            return new SimpleMailboxACL(Collections.unmodifiableMap(resultEntries), true);
-        }
+        return new SimpleMailboxACL(entries.entrySet()
+            .stream()
+            .map(entry -> Pair.of(entry.getKey(),
+                Optional.ofNullable(other.getEntries().get(entry.getKey()))
+                    .map(Throwing.function(exceptValue -> entry.getValue().except(exceptValue)))
+                    .orElse(entry.getValue())))
+            .filter(pair -> !pair.getValue().isEmpty())
+            .collect(Guavate.toImmutableMap(Entry::getKey, Entry::getValue)));
     }
     
-    /**
-     * @see org.apache.james.mailbox.model.MailboxACL#except(org.apache.james.mailbox.model.MailboxACL.MailboxACLEntryKey, org.apache.james.mailbox.model.MailboxACL.MailboxACLRights)
-     */
+    @Override
     public MailboxACL except(MailboxACLEntryKey key, MailboxACLRights mailboxACLRights) throws UnsupportedRightException {
-        Map<MailboxACLEntryKey, MailboxACLRights> resultEntries = new HashMap<>(this.entries);
-        MailboxACLRights thisRights = resultEntries.get(key);
-        if (thisRights == null) {
-            /* nothing to diff */
-        } else {
-            /* diff */
-            MailboxACLRights resultRights = thisRights.except(mailboxACLRights);
-            if (!resultRights.isEmpty()) {
-                resultEntries.put(key, resultRights);
-            }
-            else {
-                resultEntries.remove(key);
-            }
-        }
-        return new SimpleMailboxACL(Collections.unmodifiableMap(resultEntries), true);
+        return except(new SimpleMailboxACL(new SimpleMailboxACLEntry(key, mailboxACLRights)));
     }
 
-    /**
-     * @see org.apache.james.mailbox.MailboxACL#getEntries()
-     */
     @Override
     public Map<MailboxACLEntryKey, MailboxACLRights> getEntries() {
         return entries;
     }
 
-    /**
-     * @see java.lang.Object#hashCode()
-     */
-    @Override
-    public int hashCode() {
-        return entries == null ? 0 : entries.hashCode();
-    }
-
-    /**
-     * @see org.apache.james.mailbox.model.MailboxACL#replace(org.apache.james.mailbox.model.MailboxACL.MailboxACLEntryKey, org.apache.james.mailbox.model.MailboxACL.MailboxACLRights)
-     */
     @Override
     public MailboxACL replace(MailboxACLEntryKey key, MailboxACLRights replacement) throws UnsupportedRightException {
-        Map<MailboxACLEntryKey, MailboxACLRights> resultEntries = new HashMap<>(this.entries);
-        if (replacement == null || replacement.isEmpty()) {
-            resultEntries.remove(key);
+        if (entries.containsKey(key)) {
+            return new SimpleMailboxACL(
+                entries.entrySet()
+                    .stream()
+                    .map(entry -> Pair.of(entry.getKey(),
+                        entry.getKey().equals(key) ? replacement : entry.getValue()))
+                    .filter(pair -> pair.getValue() != null && !pair.getValue().isEmpty())
+                    .collect(Guavate.toImmutableMap(Pair::getKey, Pair::getValue)));
         } else {
-            resultEntries.put(key, replacement);
+            return new SimpleMailboxACL(
+                ImmutableMap.<MailboxACLEntryKey, MailboxACLRights>builder()
+                    .putAll(entries)
+                    .put(key, replacement)
+                    .build());
         }
-        return new SimpleMailboxACL(Collections.unmodifiableMap(resultEntries), true);
     }
 
-    /**
-     * @see java.lang.Object#toString()
-     */
     @Override
     public String toString() {
         return entries == null ? "" : entries.toString();
     }
 
-    /**
-     * @see org.apache.james.mailbox.MailboxACL#union(org.apache.james.mailbox.MailboxACL)
-     */
     @Override
     public MailboxACL union(MailboxACL other) throws UnsupportedRightException {
-        Map<MailboxACLEntryKey, MailboxACLRights> otherEntries = other.getEntries();
-        if (otherEntries.size() == 0) {
-            return this;
-        } else if (entries.size() == 0) {
-            return other;
-        } else {
-            int cnt = otherEntries.size() + entries.size();
-            Map<MailboxACLEntryKey, MailboxACLRights> resultEntries = new HashMap<>(cnt + cnt / 2 + 1);
-            for (Entry<MailboxACLEntryKey, MailboxACLRights> otherEntry : otherEntries.entrySet()) {
-                MailboxACLEntryKey key = otherEntry.getKey();
-                MailboxACLRights thisRights = entries.get(key);
-                if (thisRights == null) {
-                    /* nothing to union */
-                    resultEntries.put(key, otherEntry.getValue());
-                } else {
-                    /* union */
-                    resultEntries.put(key, otherEntry.getValue().union(thisRights));
-                }
-            }
-            /* let us check what we have missed in the previous loop */
-            for (Entry<MailboxACLEntryKey, MailboxACLRights> thisEntry : entries.entrySet()) {
-                MailboxACLEntryKey key = thisEntry.getKey();
-                if (!resultEntries.containsKey(key)) {
-                    resultEntries.put(key, thisEntry.getValue());
-                }
-            }
-            return new SimpleMailboxACL(Collections.unmodifiableMap(resultEntries), true);
-        }
+        return new SimpleMailboxACL(
+            Stream.concat(
+                    this.entries.entrySet().stream(),
+                    other.getEntries().entrySet().stream())
+                .collect(Guavate.toImmutableListMultimap(Entry::getKey, Entry::getValue))
+                .asMap()
+                .entrySet()
+                .stream()
+                .map(entry -> Pair.of(entry.getKey(),
+                    entry.getValue()
+                        .stream()
+                        .reduce(
+                            new Rfc4314Rights(),
+                            Throwing.binaryOperator(MailboxACLRights::union))))
+                .collect(Guavate.toImmutableMap(Pair::getKey, Pair::getValue)));
     }
     
-    /**
-     * @see org.apache.james.mailbox.model.MailboxACL#union(org.apache.james.mailbox.model.MailboxACL.MailboxACLEntryKey, org.apache.james.mailbox.model.MailboxACL.MailboxACLRights)
-     */
+    @Override
     public MailboxACL union(MailboxACLEntryKey key, MailboxACLRights mailboxACLRights) throws UnsupportedRightException {
-        Map<MailboxACLEntryKey, MailboxACLRights> resultEntries = new HashMap<>(this.entries);
-        MailboxACLRights thisRights = resultEntries.get(key);
-        if (thisRights == null) {
-            /* nothing to union */
-            resultEntries.put(key, mailboxACLRights);
-        } else {
-            /* union */
-            resultEntries.put(key, thisRights.union(mailboxACLRights));
-        }
-        return new SimpleMailboxACL(Collections.unmodifiableMap(resultEntries), true);
+        return union(new SimpleMailboxACL(new SimpleMailboxACLEntry(key, mailboxACLRights)));
     }
 
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org