Repository: james-project
Updated Branches:
  refs/heads/master 5accc88a1 -> 10d76ab6e


JAMES-2110 Define keyword on JMAP


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

Branch: refs/heads/master
Commit: 20beee7a17946b42d3464269af383338d76a463f
Parents: 5accc88
Author: quynhn <[email protected]>
Authored: Tue Aug 15 16:31:39 2017 +0700
Committer: Raphael Ouazana <[email protected]>
Committed: Thu Aug 24 15:47:26 2017 +0200

----------------------------------------------------------------------
 .../org/apache/james/jmap/model/Keyword.java    | 126 +++++++
 .../org/apache/james/jmap/model/Keywords.java   | 202 ++++++++++++
 .../org/apache/james/jmap/model/OldKeyword.java |  92 ++++++
 .../apache/james/jmap/model/KeywordTest.java    | 189 +++++++++++
 .../apache/james/jmap/model/KeywordsTest.java   | 328 +++++++++++++++++++
 .../apache/james/jmap/model/OldKeywordTest.java |  31 ++
 6 files changed, 968 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/20beee7a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keyword.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keyword.java 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keyword.java
new file mode 100644
index 0000000..ff1a2f0
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keyword.java
@@ -0,0 +1,126 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.model;
+
+import java.util.Optional;
+import javax.mail.Flags;
+
+import org.apache.commons.lang.StringUtils;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableList;
+
+public class Keyword {
+    private final static int FLAG_NAME_MIN_LENGTH = 1;
+    private final static int FLAG_NAME_MAX_LENGTH = 255;
+    private final static CharMatcher FLAG_NAME_PATTERN = 
CharMatcher.JAVA_LETTER_OR_DIGIT.or(CharMatcher.is('$'));
+
+    public final static Keyword DRAFT = new Keyword("$Draft");
+    public final static Keyword SEEN = new Keyword("$Seen");
+    public final static Keyword FLAGGED = new Keyword("$Flagged");
+    public final static Keyword ANSWERED = new Keyword("$Answered");
+    public final static Keyword DELETED = new Keyword("$Deleted");
+    public final static Keyword RECENT = new Keyword("$Recent");
+    public final static Boolean FLAG_VALUE = true;
+
+    private final static ImmutableList<Keyword> NON_EXPOSED_IMAP_KEYWORDS = 
ImmutableList.of(Keyword.RECENT, Keyword.DELETED);
+    private final static ImmutableBiMap<Flags.Flag, Keyword> IMAP_SYSTEM_FLAGS 
= ImmutableBiMap.<Flags.Flag, Keyword>builder()
+        .put(Flags.Flag.DRAFT, DRAFT)
+        .put(Flags.Flag.SEEN, SEEN)
+        .put(Flags.Flag.FLAGGED, FLAGGED)
+        .put(Flags.Flag.ANSWERED, ANSWERED)
+        .put(Flags.Flag.RECENT, RECENT)
+        .put(Flags.Flag.DELETED, DELETED)
+        .build();
+
+    private final String flagName;
+
+    public static Keyword fromFlag(Flags.Flag flag) {
+        return IMAP_SYSTEM_FLAGS.get(flag);
+    }
+
+    public Keyword(String flagName) {
+        Preconditions.checkArgument(isValid(flagName),
+                "Flagname must not be null or empty, must have length form 
1-255, must not contain charater with hex from '\u0000' to '\u00019' or {'(' 
')' '{' ']' '%' '*' '\"' '\\'} ");
+        this.flagName = flagName;
+    }
+
+    private boolean isValid(String flagName) {
+        if (StringUtils.isBlank(flagName)) {
+            return false;
+        }
+        if (flagName.length() < FLAG_NAME_MIN_LENGTH || flagName.length() > 
FLAG_NAME_MAX_LENGTH) {
+            return false;
+        }
+        if (!FLAG_NAME_PATTERN.matchesAllOf(flagName)) {
+            return false;
+        }
+        return true;
+    }
+
+    public String getFlagName() {
+        return flagName;
+    }
+
+    public boolean isExposedImapKeyword() {
+        return !NON_EXPOSED_IMAP_KEYWORDS.contains(this);
+    }
+
+    public boolean isDraft() {
+        return DRAFT.equals(this);
+    }
+
+    public Optional<Flags.Flag> asSystemFlag() {
+        return Optional.ofNullable(IMAP_SYSTEM_FLAGS.inverse()
+            .get(this));
+    }
+
+    public Flags asFlags() {
+        return asSystemFlag()
+            .map(Flags::new)
+            .orElse(new Flags(flagName));
+    }
+
+    @Override
+    public final boolean equals(Object other) {
+        if (other instanceof Keyword) {
+            Keyword otherKeyword = (Keyword) other;
+            return Objects.equal(flagName, otherKeyword.flagName);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hashCode(flagName);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("flagName", flagName)
+            .toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/20beee7a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java
new file mode 100644
index 0000000..91aff31
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java
@@ -0,0 +1,202 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.model;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+import javax.mail.Flags;
+
+import org.apache.james.mailbox.FlagsBuilder;
+
+import com.github.steveash.guavate.Guavate;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class Keywords {
+    public static final Keywords DEFAULT_VALUE = 
factory().fromSet(ImmutableSet.of());
+
+    public interface KeywordsValidator {
+        void validate(Set<Keyword> keywords);
+    }
+
+    private FlagsBuilder combiner(FlagsBuilder firstBuilder, FlagsBuilder 
secondBuilder) {
+        return firstBuilder.add(secondBuilder.build());
+    }
+
+    private FlagsBuilder accumulator(FlagsBuilder accumulator, Keyword 
keyword) {
+        return accumulator.add(keyword.asFlags());
+    }
+
+    public static class KeywordsFactory {
+        private Optional<KeywordsValidator> validator;
+        private Optional<Predicate<Keyword>> filter;
+
+        private KeywordsFactory() {
+            validator = Optional.empty();
+            filter = Optional.empty();
+        }
+
+        public KeywordsFactory throwOnImapNonExposedKeywords() {
+            validator = Optional.of(keywords -> Preconditions.checkArgument(
+                keywords.stream().allMatch(Keyword::isExposedImapKeyword), 
"Does not allow to update 'Deleted' or 'Recent' flag"));
+            return this;
+        }
+
+        public KeywordsFactory filterImapNonExposedKeywords() {
+            filter = Optional.of(keyword -> keyword.isExposedImapKeyword());
+            return this;
+        }
+
+        public Keywords fromSet(Set<Keyword> setKeywords) {
+            validator.orElse(keywords -> {})
+                .validate(setKeywords);
+
+            return new Keywords(setKeywords.stream()
+                .filter(filter.orElse(keyword -> true))
+                .collect(Guavate.toImmutableSet()));
+        }
+
+        public Keywords fromList(List<String> keywords) {
+            return fromSet(keywords.stream()
+                .map(Keyword::new)
+                .collect(Guavate.toImmutableSet()));
+        }
+
+        @VisibleForTesting
+        Keywords fromMap(Map<String, Boolean> mapKeywords) {
+            Preconditions.checkArgument(mapKeywords.values()
+                .stream()
+                .noneMatch(keywordValue -> keywordValue == false), "Keyword 
must be true");
+            Set<Keyword> setKeywords = mapKeywords.keySet()
+                .stream()
+                .map(Keyword::new)
+                .collect(Guavate.toImmutableSet());
+
+            return fromSet(setKeywords);
+        }
+
+        @VisibleForTesting
+        Keywords fromOldKeyword(OldKeyword oldKeyword) {
+            ImmutableSet.Builder<Keyword> builder = ImmutableSet.builder();
+            if (oldKeyword.isAnswered().orElse(false)) {
+                builder.add(Keyword.ANSWERED);
+            }
+            if (oldKeyword.isDraft().orElse(false)) {
+                builder.add(Keyword.DRAFT);
+            }
+            if (oldKeyword.isFlagged().orElse(false)) {
+                builder.add(Keyword.FLAGGED);
+            }
+            if (oldKeyword.isUnread().isPresent() && 
oldKeyword.isUnread().get() == false) {
+                builder.add(Keyword.SEEN);
+            }
+            return fromSet(builder.build());
+        }
+
+        public Keywords fromFlags(Flags flags) {
+            return fromSet(Stream.concat(
+                    Stream.of(flags.getUserFlags())
+                        .map(Keyword::new),
+                    Stream.of(flags.getSystemFlags())
+                        .map(Keyword::fromFlag))
+                .collect(Guavate.toImmutableSet()));
+        }
+
+        public Optional<Keywords> fromMapOrOldKeyword(Optional<Map<String, 
Boolean>> mapKeyword, Optional<OldKeyword> oldKeyword) {
+            Preconditions.checkArgument(!(mapKeyword.isPresent() && 
oldKeyword.isPresent()), "Does not support keyword and is* at the same time");
+
+            Keywords keywords = mapKeyword.map(this::fromMap)
+                .orElse(oldKeyword.map(this::fromOldKeyword)
+                    .orElse(null));
+            return Optional.ofNullable(keywords);
+        }
+    }
+
+    public static KeywordsFactory factory() {
+        return new KeywordsFactory();
+    }
+
+    private final ImmutableSet<Keyword> keywords;
+
+    private Keywords(ImmutableSet<Keyword> keywords) {
+        this.keywords = keywords;
+    }
+
+    public Flags asFlags() {
+        return keywords.stream()
+            .reduce(FlagsBuilder.builder(), this::accumulator, this::combiner)
+            .build();
+    }
+
+    public Flags asFlagsWithRecentAndDeletedFrom(Flags originFlags) {
+        Flags flags = asFlags();
+        if (originFlags.contains(Flags.Flag.DELETED)) {
+            flags.add(Flags.Flag.DELETED);
+        }
+        if (originFlags.contains(Flags.Flag.RECENT)) {
+            flags.add(Flags.Flag.RECENT);
+        }
+
+        return flags;
+    }
+
+    public ImmutableMap<String, Boolean> asMap() {
+        return keywords.stream()
+            .collect(Guavate.toImmutableMap(Keyword::getFlagName, keyword -> 
Keyword.FLAG_VALUE));
+    }
+
+    public ImmutableSet<Keyword> getKeywords() {
+        return keywords;
+    }
+
+    public boolean contains(Keyword keyword) {
+        return keywords.contains(keyword);
+    }
+
+    @Override
+    public final boolean equals(Object other) {
+        if (other instanceof Keywords) {
+            Keywords otherKeyword = (Keywords) other;
+            return Objects.equal(keywords, otherKeyword.keywords);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hashCode(keywords);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("keywords", keywords)
+            .toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/20beee7a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java
new file mode 100644
index 0000000..e2cb2b3
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java
@@ -0,0 +1,92 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.model;
+
+import java.util.Optional;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+
+public class OldKeyword {
+    private final Optional<Boolean> isUnread;
+    private final Optional<Boolean> isFlagged;
+    private final Optional<Boolean> isAnswered;
+    private final Optional<Boolean> isDraft;
+
+    @VisibleForTesting
+    OldKeyword(boolean isUnread, boolean isFlagged, boolean isAnswered, 
boolean isDraft) {
+        this.isUnread = Optional.of(isUnread);
+        this.isFlagged = Optional.of(isFlagged);
+        this.isAnswered = Optional.of(isAnswered);
+        this.isDraft = Optional.of(isDraft);
+    }
+
+    public OldKeyword(Optional<Boolean> isUnread, Optional<Boolean> isFlagged, 
Optional<Boolean> isAnswered, Optional<Boolean> isDraft) {
+        this.isUnread = isUnread;
+        this.isFlagged = isFlagged;
+        this.isAnswered = isAnswered;
+        this.isDraft = isDraft;
+    }
+
+    public Optional<Boolean> isUnread() {
+        return isUnread;
+    }
+
+    public Optional<Boolean> isFlagged() {
+        return isFlagged;
+    }
+
+    public Optional<Boolean> isAnswered() {
+        return isAnswered;
+    }
+
+    public Optional<Boolean> isDraft() {
+        return isDraft;
+    }
+
+    @Override
+    public final boolean equals(Object other) {
+        if (other instanceof OldKeyword) {
+            OldKeyword oldKeyword = (OldKeyword) other;
+            return Objects.equal(isUnread, oldKeyword.isUnread)
+                && Objects.equal(isFlagged, oldKeyword.isFlagged)
+                && Objects.equal(isAnswered, oldKeyword.isAnswered)
+                && Objects.equal(isDraft, oldKeyword.isDraft);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hashCode(isUnread, isFlagged, isAnswered, isDraft);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("isUnread", isUnread)
+                .add("isFlagged", isFlagged)
+                .add("isAnswered", isAnswered)
+                .add("isDraft", isDraft)
+                .toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/20beee7a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordTest.java
 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordTest.java
new file mode 100644
index 0000000..12fcf0a
--- /dev/null
+++ 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordTest.java
@@ -0,0 +1,189 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.model;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import java.util.Optional;
+import javax.mail.Flags;
+
+import org.apache.commons.lang3.StringUtils;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class KeywordTest {
+    private final static String FORWARDED = "forwarded";
+    private final static int FLAG_NAME_MAX_LENTH = 255;
+    private final static String ANY_KEYWORD = "AnyKeyword";
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void shouldRespectBeanContract() {
+        EqualsVerifier.forClass(Keyword.class).verify();
+    }
+
+    @Test
+    public void keywordShouldThrowWhenFlagNameLengthLessThanMinLength() throws 
Exception {
+        expectedException.expect(IllegalArgumentException.class);
+        new Keyword("");
+    }
+
+    @Test
+    public void keywordShouldThrowWhenFlagNameLengthMoreThanMaxLength() throws 
Exception {
+        expectedException.expect(IllegalArgumentException.class);
+        new Keyword(StringUtils.repeat("a", FLAG_NAME_MAX_LENTH + 1));
+    }
+
+    @Test
+    public void keywordShouldCreateNewOneWhenFlagNameLengthEqualsMaxLength() 
throws Exception {
+        String maxLengthFlagName = StringUtils.repeat("a", 
FLAG_NAME_MAX_LENTH);
+        Keyword keyword = new Keyword(maxLengthFlagName);
+
+        assertThat(keyword.getFlagName()).isEqualTo(maxLengthFlagName);
+    }
+
+    @Test
+    public void keywordShouldCreateNewOneWhenFlagNameLengthEqualsMinLength() 
throws Exception {
+        String minLengthFlagName = "a";
+        Keyword keyword = new Keyword(minLengthFlagName);
+
+        assertThat(keyword.getFlagName()).isEqualTo(minLengthFlagName);
+    }
+
+    @Test
+    public void keywordShouldThrowWhenFlagNameContainsPercentageCharacter() 
throws Exception {
+        expectedException.expect(IllegalArgumentException.class);
+        new Keyword("a%");
+    }
+
+    @Test
+    public void keywordShouldThrowWhenFlagNameContainsLeftBracket() throws 
Exception {
+        expectedException.expect(IllegalArgumentException.class);
+        new Keyword("a[");
+    }
+
+    @Test
+    public void keywordShouldThrowWhenFlagNameContainsRightBracket() throws 
Exception {
+        expectedException.expect(IllegalArgumentException.class);
+        new Keyword("a]");
+    }
+
+    @Test
+    public void keywordShouldThrowWhenFlagNameContainsLeftBrace() throws 
Exception {
+        expectedException.expect(IllegalArgumentException.class);
+        new Keyword("a{");
+    }
+
+    @Test
+    public void keywordShouldThrowWhenFlagNameContainsSlash() throws Exception 
{
+        expectedException.expect(IllegalArgumentException.class);
+        new Keyword("a\\");
+    }
+
+    @Test
+    public void keywordShouldThrowWhenFlagNameContainsStar() throws Exception {
+        expectedException.expect(IllegalArgumentException.class);
+        new Keyword("a*");
+    }
+
+    @Test
+    public void keywordShouldThrowWhenFlagNameContainsQuote() throws Exception 
{
+        expectedException.expect(IllegalArgumentException.class);
+        new Keyword("a\"");
+    }
+
+    @Test
+    public void keywordShouldThrowWhenFlagNameContainsOpeningParenthesis() 
throws Exception {
+        expectedException.expect(IllegalArgumentException.class);
+        new Keyword("a(");
+    }
+
+    @Test
+    public void keywordShouldThrowWhenFlagNameContainsClosingParenthesis() 
throws Exception {
+        expectedException.expect(IllegalArgumentException.class);
+        new Keyword("a)");
+    }
+
+    @Test
+    public void keywordShouldThrowWhenFlagNameContainsSpaceCharacter() throws 
Exception {
+        expectedException.expect(IllegalArgumentException.class);
+        new Keyword("a b");
+    }
+
+    @Test
+    public void isNotNonExposedImapKeywordShouldReturnFalseWhenDeleted() 
throws Exception {
+        assertThat(Keyword.DELETED.isExposedImapKeyword()).isFalse();
+    }
+
+    @Test
+    public void isNotNonExposedImapKeywordShouldReturnFalseWhenRecent() throws 
Exception {
+        assertThat(Keyword.RECENT.isExposedImapKeyword()).isFalse();
+    }
+
+    @Test
+    public void 
isNotNonExposedImapKeywordShouldReturnTrueWhenOtherSystemFlag() throws 
Exception {
+        assertThat(Keyword.DRAFT.isExposedImapKeyword()).isTrue();
+    }
+
+    @Test
+    public void isNotNonExposedImapKeywordShouldReturnTrueWhenAnyUserFlag() 
throws Exception {
+        Keyword keyword = new Keyword(ANY_KEYWORD);
+        assertThat(keyword.isExposedImapKeyword()).isTrue();
+    }
+
+    @Test
+    public void isDraftShouldReturnTrueWhenDraft() throws Exception {
+        assertThat(Keyword.DRAFT.isDraft()).isTrue();
+    }
+
+    @Test
+    public void isDraftShouldReturnFalseWhenNonDraft() throws Exception {
+        assertThat(Keyword.DELETED.isDraft()).isFalse();
+    }
+
+    @Test
+    public void asSystemFlagShouldReturnSystemFlag() throws Exception {
+        assertThat(new Keyword("$Draft").asSystemFlag())
+            .isEqualTo(Optional.of(Flags.Flag.DRAFT));
+    }
+
+    @Test
+    public void asSystemFlagShouldReturnEmptyWhenNonSystemFlag() throws 
Exception {
+        assertThat(new Keyword(ANY_KEYWORD).asSystemFlag().isPresent())
+            .isFalse();
+    }
+
+    @Test
+    public void asFlagsShouldReturnFlagsWhenSystemFlag() throws Exception {
+        assertThat(Keyword.DELETED.asFlags())
+            .isEqualTo(new Flags(Flags.Flag.DELETED));
+    }
+
+    @Test
+    public void asFlagsShouldReturnFlagsWhenUserFlag() throws Exception {
+        Keyword keyword = new Keyword(ANY_KEYWORD);
+        assertThat(keyword.asFlags())
+            .isEqualTo(new Flags(ANY_KEYWORD));
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/20beee7a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java
 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java
new file mode 100644
index 0000000..e992c59
--- /dev/null
+++ 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java
@@ -0,0 +1,328 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.model;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import java.util.Map;
+import java.util.Optional;
+import javax.mail.Flags;
+import javax.mail.Flags.Flag;
+
+import org.apache.james.mailbox.FlagsBuilder;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class KeywordsTest {
+    public static final String ANY_KEYWORD = "AnyKeyword";
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void shouldRespectBeanContract() {
+        EqualsVerifier.forClass(Keywords.class).verify();
+    }
+
+    @Test
+    public void fromMapShouldThrowWhenWrongKeywordValue() throws Exception {
+        expectedException.expect(IllegalArgumentException.class);
+
+        Keywords.factory()
+            .fromMap(ImmutableMap.of(ANY_KEYWORD, false));
+    }
+
+    @Test
+    public void fromMapShouldReturnKeywordsFromMapStringAndBoolean() throws 
Exception {
+        Keywords keywords = Keywords.factory()
+            .fromMap(ImmutableMap.of(ANY_KEYWORD, Keyword.FLAG_VALUE));
+
+        assertThat(keywords.getKeywords())
+            .containsOnly(new Keyword(ANY_KEYWORD));
+    }
+
+    @Test
+    public void fromFlagsShouldReturnKeywordsFromAllFlag() throws Exception {
+        Keywords keywords = Keywords.factory()
+            .fromFlags(new Flags(Flags.Flag.ANSWERED));
+
+        assertThat(keywords.getKeywords())
+            .containsOnly(Keyword.ANSWERED);
+    }
+
+    @Test
+    public void fromSetShouldReturnKeywordsFromSetOfKeywords() throws 
Exception {
+        Keywords keywords = Keywords.factory()
+            .fromSet(ImmutableSet.of(Keyword.ANSWERED));
+
+        assertThat(keywords.getKeywords())
+            .containsOnly(Keyword.ANSWERED);
+    }
+
+    @Test
+    public void fromOldKeywordsShouldReturnKeywordsFromIsAnswered() throws 
Exception {
+        Optional<Boolean> isAnswered = Optional.of(true);
+        Optional<Boolean> isUnread = Optional.empty();
+        Optional<Boolean> isFlagged = Optional.empty();
+        Optional<Boolean> isDraft = Optional.empty();
+        Keywords keywords = Keywords.factory()
+            .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, 
isDraft));
+
+        assertThat(keywords.getKeywords())
+            .containsOnly(Keyword.ANSWERED);
+    }
+
+    @Test
+    public void fromOldKeywordsShouldReturnKeywordsFromIsDraft() throws 
Exception {
+        Optional<Boolean> isAnswered = Optional.empty();
+        Optional<Boolean> isUnread = Optional.empty();
+        Optional<Boolean> isFlagged = Optional.empty();
+        Optional<Boolean> isDraft = Optional.of(true);
+        Keywords keywords = Keywords.factory()
+            .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, 
isDraft));
+
+        assertThat(keywords.getKeywords())
+            .containsOnly(Keyword.DRAFT);
+    }
+
+    @Test
+    public void fromOldKeywordsShouldReturnKeywordsFromIsFlagged() throws 
Exception {
+        Optional<Boolean> isAnswered = Optional.empty();
+        Optional<Boolean> isUnread = Optional.empty();
+        Optional<Boolean> isFlagged = Optional.of(true);
+        Optional<Boolean> isDraft = Optional.empty();
+        Keywords keywords = Keywords.factory()
+            .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, 
isDraft));
+
+        assertThat(keywords.getKeywords())
+            .containsOnly(Keyword.FLAGGED);
+    }
+
+    @Test
+    public void fromOldKeywordsShouldReturnKeywordsFromIsUnread() throws 
Exception {
+        Optional<Boolean> isAnswered = Optional.empty();
+        Optional<Boolean> isUnread = Optional.of(false);
+        Optional<Boolean> isFlagged = Optional.empty();
+        Optional<Boolean> isDraft = Optional.empty();
+        Keywords keywords = Keywords.factory()
+            .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, 
isDraft));
+
+        assertThat(keywords.getKeywords())
+            .containsOnly(Keyword.SEEN);
+    }
+
+    @Test
+    public void fromOldKeywordsShouldReturnKeywordsFromIsFlag() throws 
Exception {
+        Optional<Boolean> isAnswered = Optional.of(true);
+        Optional<Boolean> isUnread = Optional.of(true);
+        Optional<Boolean> isFlagged = Optional.of(true);
+        Optional<Boolean> isDraft = Optional.of(true);
+        Keywords keywords = Keywords.factory()
+            .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, 
isDraft));
+
+        assertThat(keywords.getKeywords())
+            .containsOnly(Keyword.ANSWERED, Keyword.DRAFT, Keyword.FLAGGED);
+    }
+
+    @Test
+    public void fromMapOrOldKeywordShouldReturnEmptyWhenBothAreEmpty() throws 
Exception {
+        assertThat(Keywords.factory()
+            .fromMapOrOldKeyword(Optional.empty(), Optional.empty()))
+            .isEmpty();
+    }
+
+    @Test
+    public void fromMapOrOldKeywordShouldReturnKeywordsWhenMap() throws 
Exception {
+        assertThat(Keywords.factory()
+            .fromMapOrOldKeyword(Optional.of(ImmutableMap.of()), 
Optional.empty()))
+            .isPresent();
+    }
+
+    @Test
+    public void fromMapOrOldKeywordShouldReturnKeywordsWhenOldKeyword() throws 
Exception {
+        Optional<Boolean> isAnswered = Optional.of(true);
+        Optional<Boolean> isUnread = Optional.of(true);
+        Optional<Boolean> isFlagged = Optional.of(true);
+        Optional<Boolean> isDraft = Optional.of(true);
+
+        OldKeyword oldKeyword = new OldKeyword(isUnread, isFlagged, 
isAnswered, isDraft);
+
+        assertThat(Keywords.factory()
+            .fromMapOrOldKeyword(Optional.empty(), Optional.of(oldKeyword)))
+            .isPresent();
+    }
+
+    @Test
+    public void fromMapOrOldKeywordShouldThrownWhenBothMapAndOldKeyword() 
throws Exception {
+        expectedException.expect(IllegalArgumentException.class);
+        Optional<Boolean> isAnswered = Optional.of(true);
+        Optional<Boolean> isUnread = Optional.of(true);
+        Optional<Boolean> isFlagged = Optional.of(true);
+        Optional<Boolean> isDraft = Optional.of(true);
+
+        OldKeyword oldKeyword = new OldKeyword(isUnread, isFlagged, 
isAnswered, isDraft);
+
+        Keywords.factory()
+            .fromMapOrOldKeyword(Optional.of(ImmutableMap.of()), 
Optional.of(oldKeyword));
+    }
+
+    @Test
+    public void fromMapOrOldKeywordShouldReturnKeywordsFromMap() throws 
Exception {
+        ImmutableMap<String, Boolean> mapKeywords = 
ImmutableMap.of(ANY_KEYWORD, Keyword.FLAG_VALUE);
+
+        assertThat(Keywords.factory()
+            .fromMapOrOldKeyword(Optional.of(mapKeywords), Optional.empty())
+            .get()
+            .getKeywords())
+            .containsOnly(new Keyword(ANY_KEYWORD));
+    }
+
+    @Test
+    public void fromMapOrOldKeywordShouldReturnKeywordsFromOldKeyword() throws 
Exception {
+        Optional<Boolean> isAnswered = Optional.of(true);
+        Optional<Boolean> isUnread = Optional.of(true);
+        Optional<Boolean> isFlagged = Optional.of(true);
+        Optional<Boolean> isDraft = Optional.of(true);
+
+        OldKeyword oldKeyword = new OldKeyword(isUnread, isFlagged, 
isAnswered, isDraft);
+
+        Keywords keywords = Keywords.factory()
+            .fromMapOrOldKeyword(Optional.empty(), Optional.of(oldKeyword))
+            .get();
+        assertThat(keywords.getKeywords())
+            .containsOnly(Keyword.ANSWERED, Keyword.DRAFT, Keyword.FLAGGED);
+    }
+
+    @Test
+    public void asFlagsShouldBuildFlagsFromKeywords() throws Exception {
+        assertThat(Keywords.factory()
+                .fromSet(ImmutableSet.of(Keyword.ANSWERED))
+                .asFlags())
+            .isEqualTo(new Flags(Flags.Flag.ANSWERED));
+    }
+
+    @Test
+    public void 
asFlagsWithRecentAndDeletedFromShouldBuildFlagsFromKeywordsAndRecentOriginFlags()
 throws Exception {
+        Flags originFlags = FlagsBuilder.builder()
+            .add(Flag.RECENT, Flag.DRAFT)
+            .build();
+
+        Flags expectedFlags = FlagsBuilder.builder()
+            .add(Flag.ANSWERED, Flag.RECENT)
+            .build();
+
+        assertThat(Keywords.factory()
+                .fromSet(ImmutableSet.of(Keyword.ANSWERED))
+                .asFlagsWithRecentAndDeletedFrom(originFlags))
+            .isEqualTo(expectedFlags);
+    }
+
+    @Test
+    public void 
asFlagsWithRecentAndDeletedFromShouldBuildFlagsFromKeywordsWithDeletedAndRecentOriginFlags()
 throws Exception {
+        Flags originFlags = FlagsBuilder.builder()
+            .add(Flag.RECENT, Flag.DELETED, Flag.DRAFT)
+            .build();
+
+        Flags expectedFlags = FlagsBuilder.builder()
+            .add(Flag.ANSWERED, Flag.RECENT, Flag.DELETED)
+            .build();
+
+        assertThat(Keywords.factory()
+                .fromSet(ImmutableSet.of(Keyword.ANSWERED))
+                .asFlagsWithRecentAndDeletedFrom(originFlags))
+            .isEqualTo(expectedFlags);
+    }
+
+    @Test
+    public void asMapShouldReturnEmptyWhenEmptyMapOfStringAndBoolean() throws 
Exception {
+        assertThat(Keywords.factory()
+                .fromSet(ImmutableSet.of())
+                .asMap())
+            .isEmpty();;
+    }
+
+    @Test
+    public void asMapShouldReturnMapOfStringAndBoolean() throws Exception {
+        Map<String, Boolean> expectedMap = ImmutableMap.of("$Answered", 
Keyword.FLAG_VALUE);
+        assertThat(Keywords.factory()
+                .fromSet(ImmutableSet.of(Keyword.ANSWERED))
+                .asMap())
+            .isEqualTo(expectedMap);
+    }
+
+    @Test
+    public void 
throwWhenUnsupportedKeywordShouldThrowWhenHaveUnsupportedKeywords() throws 
Exception {
+        expectedException.expect(IllegalArgumentException.class);
+
+        Keywords.factory()
+            .throwOnImapNonExposedKeywords()
+            .fromSet(ImmutableSet.of(Keyword.DRAFT, Keyword.DELETED));
+    }
+
+    @Test
+    public void throwWhenUnsupportedKeywordShouldNotThrowWhenHaveDraft() 
throws Exception {
+        Keywords keywords = Keywords.factory()
+            .throwOnImapNonExposedKeywords()
+            .fromSet(ImmutableSet.of(Keyword.ANSWERED, Keyword.DRAFT));
+
+        assertThat(keywords.getKeywords())
+            .containsOnly(Keyword.ANSWERED, Keyword.DRAFT);
+    }
+
+    @Test
+    public void filterUnsupportedShouldFilter() throws Exception {
+        Keywords keywords = Keywords.factory()
+            .filterImapNonExposedKeywords()
+            .fromSet(ImmutableSet.of(Keyword.ANSWERED, Keyword.DELETED, 
Keyword.RECENT, Keyword.DRAFT));
+
+        assertThat(keywords.getKeywords())
+            .containsOnly(Keyword.ANSWERED, Keyword.DRAFT);
+    }
+
+    @Test
+    public void containsShouldReturnTrueWhenKeywordsContainKeyword() {
+        Keywords keywords = Keywords.factory()
+            .fromSet(ImmutableSet.of(Keyword.SEEN));
+
+        assertThat(keywords.contains(Keyword.SEEN)).isTrue();
+    }
+
+    @Test
+    public void containsShouldReturnFalseWhenKeywordsDoNotContainKeyword() {
+        Keywords keywords = Keywords.factory()
+            .fromSet(ImmutableSet.of());
+
+        assertThat(keywords.contains(Keyword.SEEN)).isFalse();
+    }
+
+    @Test
+    public void fromListShouldReturnKeywordsFromListOfStrings() throws 
Exception {
+        Keywords keywords = Keywords.factory()
+            .fromList(ImmutableList.of("$Answered", "$Flagged"));
+
+        assertThat(keywords.getKeywords())
+            .containsOnly(Keyword.ANSWERED, Keyword.FLAGGED);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/20beee7a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java
 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java
new file mode 100644
index 0000000..024271a
--- /dev/null
+++ 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java
@@ -0,0 +1,31 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.model;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.Test;
+
+public class OldKeywordTest {
+    @Test
+    public void shouldRespectBeanContract() {
+        EqualsVerifier.forClass(OldKeyword.class).verify();
+    }
+
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to