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 <btell...@linagora.com> Authored: Mon Sep 25 11:35:29 2017 +0700 Committer: Matthieu Baechler <matth...@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-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org