Java tutorial
/* ***** BEGIN LICENSE BLOCK ***** * * Copyright (C) 2011-2014 Linagora * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version, provided you comply * with the Additional Terms applicable for OBM connector by Linagora * pursuant to Section 7 of the GNU Affero General Public License, * subsections (b), (c), and (e), pursuant to which you must notably (i) retain * the Message sent thanks to OBM, Free Communication by Linagora? * signature notice appended to any and all outbound messages * (notably e-mail and meeting requests), (ii) retain all hypertext links between * OBM and obm.org, as well as between Linagora and linagora.com, and (iii) refrain * from infringing Linagora intellectual property rights over its trademarks * and commercial brands. Other Additional Terms apply, * see <http://www.linagora.com/licenses/> for more details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License * for more details. * * You should have received a copy of the GNU Affero General Public License * and its applicable Additional Terms for OBM along with this program. If not, * see <http://www.gnu.org/licenses/> for the GNU Affero General Public License version 3 * and <http://www.linagora.com/licenses/> for the Additional Terms applicable to * OBM connectors. * * ***** END LICENSE BLOCK ***** */ package org.obm.push.mail; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Set; import org.obm.push.bean.change.WindowingChanges; import org.obm.push.bean.change.WindowingChangesBuilder; import org.obm.push.mail.bean.Email; import com.google.common.base.Function; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import com.google.common.primitives.Longs; public class EmailChanges implements WindowingChanges<Email>, Serializable { public static EmailChanges empty() { return builder().build(); } public static Builder builder() { return new Builder(); } public static class Builder implements WindowingChangesBuilder<Email> { private final SortedEmailSet additions; private final SortedEmailSet changes; private final SortedEmailSet deletions; private Builder() { super(); additions = new SortedEmailSet(); changes = new SortedEmailSet(); deletions = new SortedEmailSet(); } @Override public Class<Email> getPIMDataClass() { return Email.class; } @Override public Builder deletion(Email email) { this.deletions.add(email); return this; } public Builder deletion(Email... emails) { this.deletions.addAll(Arrays.asList(emails)); return this; } public Builder deletions(Collection<Email> deletions) { Preconditions.checkArgument(deletions != null, "deletions must not be null"); this.deletions.addAll(deletions); return this; } @Override public Builder change(Email email) { this.changes.add(email); return this; } public Builder change(Email... emails) { this.changes.addAll(Arrays.asList(emails)); return this; } public Builder changes(Collection<Email> changes) { Preconditions.checkArgument(changes != null, "changes must not be null"); this.changes.addAll(changes); return this; } @Override public Builder addition(Email email) { this.additions.add(email); return this; } public Builder addition(Email... emails) { this.additions.addAll(Arrays.asList(emails)); return this; } public Builder additions(Collection<Email> additions) { Preconditions.checkArgument(additions != null, "additions must not be null"); this.additions.addAll(additions); return this; } public Builder merge(EmailChanges changes) { additions(changes.additions()); changes(changes.changes()); deletions(changes.deletions()); return this; } public int sumOfChanges() { return additions.size() + changes.size() + deletions.size(); } @Override public EmailChanges build() { return new EmailChanges(ImmutableSortedSet.copyOfSorted(deletions), ImmutableSortedSet.copyOfSorted(changes), ImmutableSortedSet.copyOfSorted(additions)); } } private static final long serialVersionUID = 7053066531067862573L; private final Set<Email> deletions; private final Set<Email> changes; private final Set<Email> additions; private EmailChanges(Set<Email> deletions, Set<Email> changes, Set<Email> additions) { this.deletions = deletions; this.changes = changes; this.additions = additions; } @Override public Set<Email> deletions() { return deletions; } @Override public Set<Email> changes() { return changes; } @Override public Set<Email> additions() { return additions; } public boolean hasChanges() { return !additions.isEmpty() || !changes.isEmpty() || !deletions.isEmpty(); } @Override public int sumOfChanges() { return additions.size() + changes.size() + deletions.size(); } public Splitter splitToFit(int firstPartSize) { return new Splitter(this, firstPartSize); } @Override public final int hashCode() { return Objects.hashCode(deletions, changes, additions); } @Override public final boolean equals(Object obj) { if (obj instanceof EmailChanges) { EmailChanges other = (EmailChanges) obj; return Objects.equal(this.deletions, other.deletions) && Objects.equal(this.changes, other.changes) && Objects.equal(this.additions, other.additions); } return false; } @Override public String toString() { return Objects.toStringHelper(this).add("additions", additions).add("changes", changes) .add("deletions", deletions).toString(); } public static class Splitter { private final EmailChanges fit; private final EmailChanges left; public Splitter(EmailChanges origin, int limit) { Preconditions.checkArgument(limit > 0, "limit must be a positive integer"); if (limit >= origin.sumOfChanges()) { this.fit = origin; this.left = EmailChanges.empty(); } else { Iterable<EmailPartitionEntry> entries = origin.toEntries(); this.fit = fromEntries(Iterables.limit(entries, limit)); this.left = fromEntries(Iterables.skip(entries, limit)); } } public EmailChanges getFit() { return fit; } public EmailChanges getLeft() { return left; } } private static class EmailPartitionEntry { enum Type { ADD, CHANGE, DELETION } private final Type type; private final Email email; public EmailPartitionEntry(Type type, Email email) { this.type = type; this.email = email; } public Type getType() { return type; } public Email getEmail() { return email; } } private final static class EntryProducer implements Function<Email, EmailPartitionEntry> { private final EmailPartitionEntry.Type type; public EntryProducer(EmailPartitionEntry.Type type) { this.type = type; } @Override public EmailPartitionEntry apply(Email input) { return new EmailPartitionEntry(type, input); } } private Iterable<EmailPartitionEntry> toEntries() { return FluentIterable .from(Iterables.concat( FluentIterable.from(additions).transform(new EntryProducer(EmailPartitionEntry.Type.ADD)), FluentIterable.from(changes).transform(new EntryProducer(EmailPartitionEntry.Type.CHANGE)), FluentIterable.from(deletions) .transform(new EntryProducer(EmailPartitionEntry.Type.DELETION)))) .toSortedList(new Comparator<EmailPartitionEntry>() { @Override public int compare(EmailPartitionEntry o1, EmailPartitionEntry o2) { return Longs.compare(o2.getEmail().getUid(), o1.getEmail().getUid()); } }); } private static EmailChanges fromEntries(Iterable<EmailPartitionEntry> entries) { Builder builder = EmailChanges.builder(); for (EmailPartitionEntry entry : entries) { switch (entry.getType()) { case ADD: builder.addition(entry.getEmail()); break; case CHANGE: builder.change(entry.getEmail()); break; case DELETION: builder.deletion(entry.getEmail()); break; } } return builder.build(); } public Iterable<EmailChanges> partition(int windowSize) { Preconditions.checkArgument(windowSize > 0); if (sumOfChanges() == 0) { return ImmutableList.<EmailChanges>of(); } if (sumOfChanges() < windowSize) { return ImmutableList.of(this); } return FluentIterable.from(Iterables.partition(toEntries(), windowSize)) .transform(new Function<List<EmailPartitionEntry>, EmailChanges>() { @Override public EmailChanges apply(List<EmailPartitionEntry> input) { return EmailChanges.fromEntries(input); } }); } }