org.apache.james.mailbox.model.SearchQuery Maven / Gradle / Ivy
/****************************************************************
* 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.mailbox.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.mail.Flags;
import javax.mail.Flags.Flag;
import org.apache.james.mailbox.MessageUid;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
/**
*
* Models a query used to search for messages. A query is the logical
* AND
of the contained criteria.
*
*
* Each Criterion
is composed of an Operator
* (combining value and operation) together with field information (optional
* since the criteria type may imply a particular field). Factory methods are
* provided for criteria.
*
*/
public class SearchQuery implements Serializable {
private static final long serialVersionUID = 1L;
private static final String DATE_HEADER_NAME = "Date";
/**
* The Resolution which should get used for {@link Date} searches
*/
public enum DateResolution {
Second, Minute, Hour, Day, Month, Year
}
public enum AddressType {
From, To, Cc, Bcc
}
/**
* Allow to sort a {@link SearchQuery} response in different ways.
*/
public static class Sort implements Serializable {
private static final long serialVersionUID = 1L;
public enum Order {
REVERSE,
NATURAL
}
/**
* Specify on what to sort
*/
public enum SortClause {
/**
* Internal date and time of the message (internaldate)
*/
Arrival,
/**
* addr-mailbox of the first "cc" address.
*
* This MUST BE converted to uppercase before doing the sort
*/
MailboxCc,
/**
* addr-mailbox of the first "from" address.
*
* This MUST BE converted to uppercase before doing the sort
*/
MailboxFrom,
/**
* addr-mailbox of the first "To" address
*
* This MUST BE converted to uppercase before doing the sort
*/
MailboxTo,
/**
* Base subject text.
*
* This MUST BE converted to uppercase before doing the sort
*/
BaseSubject,
/**
* Size of the message in octets.
*/
Size,
/**
*
* As used in this document, the term "sent date" refers to the date
* and time from the Date: header, adjusted by time zone to
* normalize to UTC. For example, "31 Dec 2000 16:01:33 -0800" is
* equivalent to the UTC date and time of
* "1 Jan 2001 00:01:33 +0000". If the time zone is invalid, the
* date and time SHOULD be treated as UTC. If the time is also
* invalid, the time SHOULD be treated as 00:00:00. If there is no
* valid date or time, the date and time SHOULD be treated as
* 00:00:00 on the earliest possible date.
*
* If the sent date cannot be determined (a Date: header is missing
* or cannot be parsed), the INTERNALDATE for that message is used
* as the sent date.
*
*/
SentDate,
/**
* addr-name of the first "From" address
*
* This MUST BE converted to uppercase before doing the sort
*/
DisplayFrom,
/**
* addr-name of the first "To" address
*
* This MUST BE converted to uppercase before doing the sort
*/
DisplayTo,
/**
* Uid of the message. This is the DEFAULT if no other is specified
*/
Uid,
/**
* Unique Id of the message.
*/
Id
}
private final Order order;
private final SortClause sortClause;
public Sort(SortClause sortClause, Order order) {
this.order = order;
this.sortClause = sortClause;
}
/**
* Create a new {@link Sort} which is NOT {@link #order}
*
* @param sortClause
*/
public Sort(SortClause sortClause) {
this(sortClause, Order.NATURAL);
}
/**
* Return true if the sort should be in reverse order
*
* @return reverse
*/
public boolean isReverse() {
return order == Order.REVERSE;
}
/**
* Return the {@link SortClause}
*
* @return clause
*/
public SortClause getSortClause() {
return sortClause;
}
@Override
public boolean equals(Object o) {
if (o instanceof Sort) {
Sort that = (Sort) o;
return Objects.equal(this.sortClause, that.sortClause)
&& Objects.equal(this.order, that.order);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(sortClause, order);
}
}
/**
* Creates a filter for message size less than the given value
*
* @param value
* messages with size less than this value will be selected by
* the returned criterion
* @return Criterion
, not null
*/
public static Criterion sizeLessThan(long value) {
return new SizeCriterion(new NumericOperator(value, NumericComparator.LESS_THAN));
}
/**
* Creates a filter for message size greater than the given value
*
* @param value
* messages with size greater than this value will be selected by
* the returned criterion
* @return Criterion
, not null
*/
public static Criterion sizeGreaterThan(long value) {
return new SizeCriterion(new NumericOperator(value, NumericComparator.GREATER_THAN));
}
/**
* Creates a filter for message size equal to the given value
*
* @param value
* messages with size equal to this value will be selected by the
* returned criterion
* @return Criterion
, not null
*/
public static Criterion sizeEquals(long value) {
return new SizeCriterion(new NumericOperator(value, NumericComparator.EQUALS));
}
/**
* Creates a filter for message mod-sequence less than the given value
*
* @param value
* messages with mod-sequence less than this value will be
* selected by the returned criterion
* @return Criterion
, not null
*/
public static Criterion modSeqLessThan(long value) {
return new ModSeqCriterion(new NumericOperator(value, NumericComparator.LESS_THAN));
}
/**
* Creates a filter for message mod-sequence greater than the given value
*
* @param value
* messages with mod-sequence greater than this value will be
* selected by the returned criterion
* @return Criterion
, not null
*/
public static Criterion modSeqGreaterThan(long value) {
return new ModSeqCriterion(new NumericOperator(value, NumericComparator.GREATER_THAN));
}
/**
* Creates a filter for message mod-sequence equal to the given value
*
* @param value
* messages with mod-sequence equal to this value will be
* selected by the returned criterion
* @return Criterion
, not null
*/
public static Criterion modSeqEquals(long value) {
return new ModSeqCriterion(new NumericOperator(value, NumericComparator.EQUALS));
}
/**
* Creates a filter matching messages with internal date after the given
* date.
*
* @param date
* given date
* @param dateResolution
* the date resolution, either {@link DateResolution#Year},
* {@link DateResolution#Month}, {@link DateResolution#Day},
* {@link DateResolution#Hour}, {@link DateResolution#Minute} or
* {@link DateResolution#Second}
* @return Criterion
, not null
*/
public static Criterion internalDateAfter(Date date, DateResolution dateResolution) {
return new InternalDateCriterion(new DateOperator(DateComparator.AFTER, date, dateResolution));
}
/**
* Creates a filter matching messages with internal date on the given date.
*
* @param date
* given date
* @param dateResolution
* the date resolution, either {@link DateResolution#Year},
* {@link DateResolution#Month}, {@link DateResolution#Day},
* {@link DateResolution#Hour}, {@link DateResolution#Minute} or
* {@link DateResolution#Second}
* @return Criterion
, not null
*/
public static Criterion internalDateOn(Date date, DateResolution dateResolution) {
return new InternalDateCriterion(new DateOperator(DateComparator.ON, date, dateResolution));
}
/**
* Creates a filter matching messages with internal date before the given
* date.
*
* @param date
* given date
* @param dateResolution
* the date resolution, either {@link DateResolution#Year},
* {@link DateResolution#Month}, {@link DateResolution#Day},
* {@link DateResolution#Hour}, {@link DateResolution#Minute} or
* {@link DateResolution#Second}
* @return Criterion
, not null
*/
public static Criterion internalDateBefore(Date date, DateResolution dateResolution) {
return new InternalDateCriterion(new DateOperator(DateComparator.BEFORE, date, dateResolution));
}
/**
* Creates a filter matching messages with sent date after the given
* date.
*
* @param date
* given date
* @param dateResolution
* the date resolution, either {@link DateResolution#Year},
* {@link DateResolution#Month}, {@link DateResolution#Day},
* {@link DateResolution#Hour}, {@link DateResolution#Minute} or
* {@link DateResolution#Second}
* @return Criterion
, not null
*/
public static Criterion sentDateAfter(Date date, DateResolution dateResolution) {
return headerDateAfter(DATE_HEADER_NAME, date, dateResolution);
}
/**
* Creates a filter matching messages with sent date on the given date.
*
* @param date
* given date
* @param dateResolution
* the date resolution, either {@link DateResolution#Year},
* {@link DateResolution#Month}, {@link DateResolution#Day},
* {@link DateResolution#Hour}, {@link DateResolution#Minute} or
* {@link DateResolution#Second}
* @return Criterion
, not null
*/
public static Criterion sentDateOn(Date date, DateResolution dateResolution) {
return headerDateOn(DATE_HEADER_NAME, date, dateResolution);
}
/**
* Creates a filter matching messages with sent date before the given
* date.
*
* @param date
* given date
* @param dateResolution
* the date resolution, either {@link DateResolution#Year},
* {@link DateResolution#Month}, {@link DateResolution#Day},
* {@link DateResolution#Hour}, {@link DateResolution#Minute} or
* {@link DateResolution#Second}
* @return Criterion
, not null
*/
public static Criterion sentDateBefore(Date date, DateResolution dateResolution) {
return headerDateBefore(DATE_HEADER_NAME, date, dateResolution);
}
/**
* Creates a filter matching messages with the date of the given header
* after the given date. If the header's value is not a date then it will
* not be included.
*
* @param headerName
* name of the header whose value will be compared, not null
* @param date
* given date
* @param dateResolution
* the date resolution, either {@link DateResolution#Year},
* {@link DateResolution#Month}, {@link DateResolution#Day},
* {@link DateResolution#Hour}, {@link DateResolution#Minute} or
* {@link DateResolution#Second}
* @return Criterion
, not null
*/
public static Criterion headerDateAfter(String headerName, Date date, DateResolution dateResolution) {
return new HeaderCriterion(headerName, new DateOperator(DateComparator.AFTER, date, dateResolution));
}
/**
* Creates a filter matching messages with the date of the given header on
* the given date. If the header's value is not a date then it will not be
* included.
*
* @param headerName
* name of the header whose value will be compared, not null
* @param date
* given date
* @param dateResolution
* the date resolution, either {@link DateResolution#Year},
* {@link DateResolution#Month}, {@link DateResolution#Day},
* {@link DateResolution#Hour}, {@link DateResolution#Minute} or
* {@link DateResolution#Second}
* @return Criterion
, not null
*/
public static Criterion headerDateOn(String headerName, Date date, DateResolution dateResolution) {
return new HeaderCriterion(headerName, new DateOperator(DateComparator.ON, date, dateResolution));
}
/**
* Creates a filter matching messages with the date of the given header
* before the given date. If the header's value is not a date then it will
* not be included.
*
* @param headerName
* name of the header whose value will be compared, not null
* @param date
* given date
* @param dateResolution
* the date resolution, either {@link DateResolution#Year},
* {@link DateResolution#Month}, {@link DateResolution#Day},
* {@link DateResolution#Hour}, {@link DateResolution#Minute} or
* {@link DateResolution#Second}
* @return Criterion
, not null
*/
public static Criterion headerDateBefore(String headerName, Date date, DateResolution dateResolution) {
return new HeaderCriterion(headerName, new DateOperator(DateComparator.BEFORE, date, dateResolution));
}
/**
* Creates a filter matching messages whose Address header contains the
* given address. The address header of the message MUST get canonicalized
* before try to match it.
*
* @param type
* @param address
* @return Criterion
*/
public static Criterion address(AddressType type, String address) {
return new HeaderCriterion(type.name(), new AddressOperator(address));
}
/**
* Creates a filter matching messages whose header value contains the given
* value.
*
* All to-compared Strings MUST BE converted to uppercase before doing so
* (this also include the search value)
*
* @param headerName
* name of the header whose value will be compared, not null
* @param value
* when null or empty the existance of the header will be
* checked, otherwise contained value
* @return Criterion
, not null
*/
public static Criterion headerContains(String headerName, String value) {
if (value == null || value.length() == 0) {
return headerExists(headerName);
} else {
return new HeaderCriterion(headerName, new ContainsOperator(value));
}
}
/**
* Creates a filter matching messages with a header matching the given name.
*
* All to-compared Strings MUST BE converted to uppercase before doing so
* (this also include the search value)
*
* @param headerName
* name of the header whose value will be compared, not null
* @return Criterion
, not null
*/
public static Criterion headerExists(String headerName) {
return new HeaderCriterion(headerName, ExistsOperator.exists());
}
/**
* Creates a filter matching messages which contains the given text either
* within the body or in the headers. Implementations may choose to ignore
* mime parts which cannot be decoded to text.
*
* All to-compared Strings MUST BE converted to uppercase before doing so
* (this also include the search value)
*
* @param value
* search value
* @return Criterion
, not null
*/
public static Criterion mailContains(String value) {
return new TextCriterion(value, Scope.FULL);
}
/**
* Creates a filter matching messages which contains the given text either
* within the headers (From, To, Cc, Bcc & Subject) and text / html bodies.
* Implementations may choose to ignore mime parts which cannot be decoded to text.
*
* All to-compared Strings MUST BE converted to uppercase before doing so
* (this also include the search value)
*
* @param value
* search value
* @return Criterion
, not null
*/
public static Criterion textContains(String value) {
return new TextCriterion(value, Scope.TEXT);
}
/**
* Creates a filter matching messages which contains the given text within
* the body. Implementations may choose to ignore mime parts which cannot be
* decoded to text.
*
* All to-compared Strings MUST BE converted to uppercase before doing so
* (this also include the search value)
*
* @param value
* search value
* @return Criterion
, not null
*/
public static Criterion bodyContains(String value) {
return new TextCriterion(value, Scope.BODY);
}
/**
* Creates a filter matching messages which has an attachment containing the given text.
*
* @param value
* search value
* @return Criterion
, not null
*/
public static Criterion attachmentContains(String value) {
return new TextCriterion(value, Scope.ATTACHMENTS);
}
/**
* Creates a filter matching messages within any of the given ranges.
*
* @param range
* NumericRange
's, not null
* @return Criterion
, not null
*/
public static Criterion uid(UidRange[] range) {
return new UidCriterion(range);
}
/**
* Creates a filter composing the two different criteria.
*
* @param one
* Criterion
, not null
* @param two
* Criterion
, not null
* @return Criterion
, not null
*/
public static Criterion or(Criterion one, Criterion two) {
final List criteria = new ArrayList<>();
criteria.add(one);
criteria.add(two);
return new ConjunctionCriterion(Conjunction.OR, criteria);
}
/**
* Creates a filter composing the listed criteria.
*
* @param criteria
* List
of {@link Criterion}
* @return Criterion
, not null
*/
public static Criterion or(List criteria) {
return new ConjunctionCriterion(Conjunction.OR, criteria);
}
/**
* Creates a filter composing the two different criteria.
*
* @param one
* Criterion
, not null
* @param two
* Criterion
, not null
* @return Criterion
, not null
*/
public static Criterion and(Criterion one, Criterion two) {
final List criteria = new ArrayList<>();
criteria.add(one);
criteria.add(two);
return new ConjunctionCriterion(Conjunction.AND, criteria);
}
/**
* Creates a filter composing the listed criteria.
*
* @param criteria
* List
of {@link Criterion}
* @return Criterion
, not null
*/
public static Criterion and(List criteria) {
return new ConjunctionCriterion(Conjunction.AND, criteria);
}
/**
* Creates a filter inverting the given criteria.
*
* @param criterion
* Criterion
, not null
* @return Criterion
, not null
*/
public static Criterion not(Criterion criterion) {
final List criteria = new ArrayList<>();
criteria.add(criterion);
return new ConjunctionCriterion(Conjunction.NOR, criteria);
}
/**
* Creates a filter composing the listed criteria.
*
* @param criteria
* List
of {@link Criterion}
* @return Criterion
, not null
*/
public static Criterion not(List criteria) {
return new ConjunctionCriterion(Conjunction.NOR, criteria);
}
/**
* Creates a filter on the given flag.
*
* @param flag
* Flag
, not null
* @param isSet
* true if the messages with the flag set should be matched,
* false otherwise
* @return Criterion
, not null
*/
public static Criterion flagSet(Flag flag, boolean isSet) {
final Criterion result;
if (isSet) {
result = flagIsSet(flag);
} else {
result = flagIsUnSet(flag);
}
return result;
}
public static Criterion hasAttachment(boolean value) {
if (value) {
return new AttachmentCriterion(BooleanOperator.set());
} else {
return new AttachmentCriterion(BooleanOperator.unset());
}
}
public static Criterion attachmentFileName(String fileName) {
return new TextCriterion(fileName, Scope.ATTACHMENT_FILE_NAME);
}
public static Criterion hasAttachment() {
return hasAttachment(true);
}
public static Criterion hasNoAttachment() {
return hasAttachment(false);
}
/**
* Creates a filter on the given flag selecting messages where the given
* flag is selected.
*
* @param flag
* Flag
, not null
* @return Criterion
, not null
*/
public static Criterion flagIsSet(Flag flag) {
return new FlagCriterion(flag, BooleanOperator.set());
}
/**
* Creates a filter on the given flag selecting messages where the given
* flag is not selected.
*
* @param flag
* Flag
, not null
* @return Criterion
, not null
*/
public static Criterion flagIsUnSet(Flag flag) {
return new FlagCriterion(flag, BooleanOperator.unset());
}
public static Criterion flag(Flag flag, boolean isSet) {
return new FlagCriterion(flag, new BooleanOperator(isSet));
}
/**
* Creates a filter on the given flag.
*
* @param flag
* Flag
, not null
* @param isSet
* true if the messages with the flag set should be matched,
* false otherwise
* @return Criterion
, not null
*/
public static Criterion flagSet(String flag, boolean isSet) {
final Criterion result;
if (isSet) {
result = flagIsSet(flag);
} else {
result = flagIsUnSet(flag);
}
return result;
}
/**
* Creates a filter on the given flag selecting messages where the given
* flag is selected.
*
* @param flag
* Flag
, not null
* @return Criterion
, not null
*/
public static Criterion flagIsSet(String flag) {
return new CustomFlagCriterion(flag, BooleanOperator.set());
}
/**
* Creates a filter on the given flag selecting messages where the given
* flag is not selected.
*
* @param flag
* Flag
, not null
* @return Criterion
, not null
*/
public static Criterion flagIsUnSet(String flag) {
return new CustomFlagCriterion(flag, BooleanOperator.unset());
}
/**
* Creates a filter matching all messages.
*
* @return Criterion
, not null
*/
public static Criterion all() {
return AllCriterion.all();
}
public static Criterion mimeMessageID(String messageId) {
return new MimeMessageIDCriterion(messageId);
}
private final Set recentMessageUids = new HashSet<>();
private final List criterias;
private List sorts = Collections.singletonList(new Sort(Sort.SortClause.Uid, Sort.Order.NATURAL));
public SearchQuery(Criterion... criterias) {
this(new ArrayList<>(Arrays.asList(criterias)));
}
public SearchQuery() {
this(new ArrayList<>());
}
private SearchQuery(List criterias) {
this.criterias = criterias;
}
public void andCriteria(Criterion crit) {
criterias.add(crit);
}
public List getCriterias() {
return criterias;
}
/**
* Set the {@link Sort}'s to use
*
* @param sorts
*/
public void setSorts(List sorts) {
if (sorts == null || sorts.isEmpty()) {
throw new IllegalArgumentException("There must be at least one Sort");
}
this.sorts = sorts;
}
/**
* Return the {@link Sort}'s which should get used for sorting the result.
* They get "executed" in a chain, if the first does not give an result the
* second will get executed etc.
*
* If not set via {@link #setSorts(List)} it will sort via
* {@link Sort.SortClause#Uid}
*
* @return sorts
*/
public List getSorts() {
return sorts;
}
/**
* Gets the UIDS of messages which are recent for this client session. The
* list of recent mail is maintained in the protocol layer since the
* mechanics are protocol specific.
*
* @return mutable Set
of MessageUid
UIDS
*/
public Set getRecentMessageUids() {
return recentMessageUids;
}
/**
* Adds all the uids to the collection of recents.
*
* @param uids
* not null
*/
public void addRecentMessageUids(Collection uids) {
recentMessageUids.addAll(uids);
}
@Override
public String toString() {
return "Search:" + criterias.toString();
}
@Override
public final int hashCode() {
return Objects.hashCode(criterias);
}
@Override
public final boolean equals(Object obj) {
if (obj instanceof SearchQuery) {
SearchQuery that = (SearchQuery) obj;
return Objects.equal(this.criterias, that.criterias);
}
return false;
}
/**
* Numbers within a particular range. Range includes both high and low
* boundaries. May be a single value. {@link Long#MAX_VALUE} represents
* unlimited in either direction.
*/
public static class NumericRange implements Serializable {
private static final long serialVersionUID = 1L;
private final long lowValue;
private final long highValue;
public NumericRange(long value) {
super();
this.lowValue = value;
this.highValue = value;
}
public NumericRange(long lowValue, long highValue) {
super();
this.lowValue = lowValue;
this.highValue = highValue;
}
public long getHighValue() {
return highValue;
}
public long getLowValue() {
return lowValue;
}
/**
* Is the given value in this range?
*
* @param value
* value to be tested
* @return true if the value is in range, false otherwise
*/
public boolean isIn(long value) {
if (lowValue == Long.MAX_VALUE) {
return highValue >= value;
}
return lowValue <= value && highValue >= value;
}
@Override
public int hashCode() {
return Objects.hashCode(lowValue, highValue);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof NumericRange) {
NumericRange that = (NumericRange) obj;
return Objects.equal(this.lowValue, that.lowValue)
&& Objects.equal(this.highValue, that.highValue);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("lowValue", lowValue)
.add("highValue", highValue)
.toString();
}
}
/**
* Numbers within a particular range. Range includes both high and low
* boundaries. May be a single value. {@link Long#MAX_VALUE} represents
* unlimited in either direction.
*/
public static class UidRange implements Serializable {
private final MessageUid lowValue;
private final MessageUid highValue;
public UidRange(MessageUid value) {
super();
this.lowValue = value;
this.highValue = value;
}
public UidRange(MessageUid lowValue, MessageUid highValue) {
super();
this.lowValue = lowValue;
this.highValue = highValue;
}
public MessageUid getHighValue() {
return highValue;
}
public MessageUid getLowValue() {
return lowValue;
}
/**
* Is the given value in this range?
*
* @param value
* value to be tested
* @return true if the value is in range, false otherwise
*/
public boolean isIn(MessageUid value) {
return lowValue.compareTo(value) <= 0 && highValue.compareTo(value) >= 0;
}
@Override
public int hashCode() {
return Objects.hashCode(lowValue, highValue);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof UidRange) {
UidRange that = (UidRange) obj;
return Objects.equal(this.lowValue, that.lowValue)
&& Objects.equal(this.highValue, that.highValue);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("lowValue", lowValue)
.add("highValue", highValue)
.toString();
}
}
/**
* Marker superclass for criteria.
*/
public abstract static class Criterion implements Serializable {
private static final long serialVersionUID = 1L;
}
public enum Conjunction {
AND, OR, NOR
}
/**
* Conjunction applying to the contained criteria. {@link #getType}
* indicates how the conjoined criteria should be related.
*/
public static class ConjunctionCriterion extends Criterion {
private static final long serialVersionUID = 1L;
private final Conjunction type;
private final List criteria;
public ConjunctionCriterion(Conjunction type, List criteria) {
super();
this.type = type;
this.criteria = criteria;
}
/**
* Gets the criteria related through this conjunction.
*
* @return List
of {@link Criterion}
*/
public List getCriteria() {
return criteria;
}
/**
* Gets the type of conjunction.
*
* @return not null
*/
public Conjunction getType() {
return type;
}
@Override
public int hashCode() {
return Objects.hashCode(criteria);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ConjunctionCriterion) {
ConjunctionCriterion that = (ConjunctionCriterion) obj;
return Objects.equal(this.criteria, that.criteria);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("criteria", criteria)
.add("type", type)
.toString();
}
}
/**
* Any message.
*/
public static class AllCriterion extends Criterion {
private static final long serialVersionUID = 1L;
private static final AllCriterion ALL = new AllCriterion();
private static Criterion all() {
return ALL;
}
@Override
public boolean equals(Object obj) {
return obj instanceof AllCriterion;
}
@Override
public int hashCode() {
return 1729;
}
@Override
public String toString() {
return "AllCriterion";
}
}
public enum Scope {
/** Only message body content */
BODY,
/** Headers: From, To, Cc, Bcc & Subjects
* plus text/plain & text/html part
*/
TEXT,
/** Full message content including headers and attachments */
FULL,
/** Attachment content */
ATTACHMENTS,
/** Attachment file name, specified on Content-Disposition
* header of mime body parts */
ATTACHMENT_FILE_NAME
}
/**
* Message text.
*/
public static class TextCriterion extends Criterion {
private static final long serialVersionUID = 1L;
private final Scope type;
private final ContainsOperator operator;
private TextCriterion(String value, Scope type) {
super();
this.operator = new ContainsOperator(value);
this.type = type;
}
/**
* Gets the type of text to be searched.
*
* @return not null
*/
public Scope getType() {
return type;
}
/**
* Gets the search operation and value to be evaluated.
*
* @return the Operator
, not null
*/
public ContainsOperator getOperator() {
return operator;
}
@Override
public int hashCode() {
return Objects.hashCode(operator);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof TextCriterion) {
TextCriterion that = (TextCriterion) obj;
return Objects.equal(this.operator, that.operator);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("operator", operator)
.toString();
}
}
public static class MimeMessageIDCriterion extends Criterion {
private final String messageID;
public MimeMessageIDCriterion(String messageID) {
this.messageID = messageID;
}
public String getMessageID() {
return messageID;
}
public HeaderCriterion asHeaderCriterion() {
return new HeaderCriterion("Message-ID", new ContainsOperator(messageID));
}
@Override
public final boolean equals(Object o) {
if (o instanceof MimeMessageIDCriterion) {
MimeMessageIDCriterion that = (MimeMessageIDCriterion) o;
return java.util.Objects.equals(this.messageID, that.messageID);
}
return false;
}
@Override
public final int hashCode() {
return java.util.Objects.hash(messageID);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("messageID", messageID)
.toString();
}
}
/**
* Header value content search.
*/
public static class HeaderCriterion extends Criterion {
private static final long serialVersionUID = 1L;
private final HeaderOperator operator;
private final String headerName;
private HeaderCriterion(String headerName, HeaderOperator operator) {
super();
this.operator = operator;
this.headerName = headerName;
}
/**
* Gets the name of the header whose value is to be searched.
*
* @return the headerName
*/
public String getHeaderName() {
return headerName;
}
/**
* Gets the search operation and value to be evaluated.
*
* @return the Operator
, not null
*/
public HeaderOperator getOperator() {
return operator;
}
@Override
public int hashCode() {
return Objects.hashCode(headerName, operator);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof HeaderCriterion) {
HeaderCriterion that = (HeaderCriterion) obj;
return Objects.equal(this.operator, that.operator)
&& Objects.equal(this.headerName, that.headerName);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("operator", operator)
.add("headerName", headerName)
.toString();
}
}
/**
* Filters on the internal date.
*/
public static class InternalDateCriterion extends Criterion {
private static final long serialVersionUID = 1L;
private final DateOperator operator;
public InternalDateCriterion(DateOperator operator) {
super();
this.operator = operator;
}
/**
* Gets the search operation and value to be evaluated.
*
* @return the Operator
, not null
*/
public DateOperator getOperator() {
return operator;
}
@Override
public int hashCode() {
return Objects.hashCode(operator);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof InternalDateCriterion) {
InternalDateCriterion that = (InternalDateCriterion) obj;
return Objects.equal(this.operator, that.operator);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("operator", operator)
.toString();
}
}
/**
* Filters on the mod-sequence of the messages.
*/
public static class ModSeqCriterion extends Criterion {
private static final long serialVersionUID = 1L;
private final NumericOperator operator;
private ModSeqCriterion(NumericOperator operator) {
super();
this.operator = operator;
}
/**
* Gets the search operation and value to be evaluated.
*
* @return the NumericOperator
, not null
*/
public NumericOperator getOperator() {
return operator;
}
@Override
public int hashCode() {
return Objects.hashCode(operator);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ModSeqCriterion) {
ModSeqCriterion that = (ModSeqCriterion) obj;
return Objects.equal(this.operator, that.operator);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("operator", operator)
.toString();
}
}
public static class SizeCriterion extends Criterion {
private static final long serialVersionUID = 1L;
private final NumericOperator operator;
private SizeCriterion(NumericOperator operator) {
super();
this.operator = operator;
}
/**
* Gets the search operation and value to be evaluated.
*
* @return the NumericOperator
, not null
*/
public NumericOperator getOperator() {
return operator;
}
@Override
public int hashCode() {
return Objects.hashCode(operator);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SizeCriterion) {
SizeCriterion that = (SizeCriterion) obj;
return Objects.equal(this.operator, that.operator);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("operator", operator)
.toString();
}
}
/**
* Filters on a custom flag valuation.
*/
public static class CustomFlagCriterion extends Criterion {
private static final long serialVersionUID = 1L;
private final String flag;
private final BooleanOperator operator;
private CustomFlagCriterion(String flag, BooleanOperator operator) {
super();
this.flag = flag;
this.operator = operator;
}
/**
* Gets the custom flag to be search.
*
* @return the flag name, not null
*/
public String getFlag() {
return flag;
}
/**
* Gets the value to be tested.
*
* @return the BooleanOperator
, not null
*/
public BooleanOperator getOperator() {
return operator;
}
@Override
public int hashCode() {
return Objects.hashCode(flag, operator);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof CustomFlagCriterion) {
CustomFlagCriterion that = (CustomFlagCriterion) obj;
return Objects.equal(this.operator, that.operator)
&& Objects.equal(this.flag, that.flag);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("operator", operator)
.add("flag", flag)
.toString();
}
}
/***
* Filter on attachment presence
*/
public static class AttachmentCriterion extends Criterion {
private final BooleanOperator operator;
private AttachmentCriterion(BooleanOperator operator) {
this.operator = operator;
}
/**
* Gets the test to be preformed.
*
* @return the BooleanOperator
, not null
*/
public BooleanOperator getOperator() {
return operator;
}
@Override
public int hashCode() {
return Objects.hashCode(operator);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof AttachmentCriterion) {
AttachmentCriterion that = (AttachmentCriterion) obj;
return Objects.equal(this.operator, that.operator);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("operator", operator)
.toString();
}
}
/**
* Filters on a standard flag.
*/
public static class FlagCriterion extends Criterion {
private static final long serialVersionUID = 1L;
// Flags not Flag because Flags are serializable and Flag is not
private final Flags flag;
private final BooleanOperator operator;
private FlagCriterion(Flag flag, BooleanOperator operator) {
super();
this.flag = new Flags(flag);
this.operator = operator;
}
/**
* Gets the flag filtered on.
*
* @return the flag, not null
*/
public Flag getFlag() {
// safe because the Flags(Flag) does system flags,
// and James code also constructs FlagCriterion only with system flags
return flag.getSystemFlags()[0];
}
/**
* Gets the test to be preformed.
*
* @return the BooleanOperator
, not null
*/
public BooleanOperator getOperator() {
return operator;
}
@Override
public int hashCode() {
return Objects.hashCode(flag, operator);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof FlagCriterion) {
FlagCriterion that = (FlagCriterion) obj;
return Objects.equal(this.operator, that.operator)
&& Objects.equal(this.flag, that.flag);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("operator", operator)
.add("flag", flag)
.toString();
}
}
/**
* Filters on message identity.
*/
public static class UidCriterion extends Criterion {
private static final long serialVersionUID = 1L;
private final UidInOperator operator;
public UidCriterion(UidRange[] ranges) {
super();
this.operator = new UidInOperator(ranges);
}
/**
* Gets the filtering operation.
*/
public UidInOperator getOperator() {
return operator;
}
@Override
public int hashCode() {
return Objects.hashCode(operator);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof UidCriterion) {
UidCriterion that = (UidCriterion) obj;
return Objects.equal(this.operator, that.operator);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("operator", operator)
.toString();
}
}
/**
* Search operator.
*/
public interface Operator extends Serializable {
}
/**
* Marks operator as suitable for header value searching.
*/
public interface HeaderOperator extends Operator {
}
public static class AddressOperator implements HeaderOperator {
private static final long serialVersionUID = 1L;
private final String address;
public AddressOperator(String address) {
super();
this.address = address;
}
/**
* Gets the value to be searched for.
*
* @return the value
*/
public String getAddress() {
return address;
}
@Override
public int hashCode() {
return Objects.hashCode(address);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof AddressOperator) {
AddressOperator that = (AddressOperator) obj;
return Objects.equal(this.address, that.address);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("address", address)
.toString();
}
}
/**
* Contained value search.
*/
public static class ContainsOperator implements HeaderOperator {
private static final long serialVersionUID = 1L;
private final String value;
public ContainsOperator(String value) {
super();
this.value = value;
}
/**
* Gets the value to be searched for.
*
* @return the value
*/
public String getValue() {
return value;
}
@Override
public int hashCode() {
return Objects.hashCode(value);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ContainsOperator) {
ContainsOperator that = (ContainsOperator) obj;
return Objects.equal(this.value, that.value);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("value", value)
.toString();
}
}
/**
* Existance search.
*/
public static class ExistsOperator implements HeaderOperator {
private static final long serialVersionUID = 1L;
private static final ExistsOperator EXISTS = new ExistsOperator();
public static ExistsOperator exists() {
return EXISTS;
}
@Override
public boolean equals(Object obj) {
return obj instanceof ExistsOperator;
}
@Override
public int hashCode() {
return 42;
}
@Override
public String toString() {
return "ExistsCriterion";
}
}
/**
* Boolean value search.
*/
public static class BooleanOperator implements Operator {
private static final long serialVersionUID = 1L;
private static final BooleanOperator SET = new BooleanOperator(true);
private static final BooleanOperator UNSET = new BooleanOperator(false);
public static BooleanOperator set() {
return SET;
}
public static BooleanOperator unset() {
return UNSET;
}
private final boolean set;
private BooleanOperator(boolean set) {
super();
this.set = set;
}
/**
* Is the search for set?
*
* @return true indicates that set values should be selected, false
* indicates that unset values should be selected
*/
public boolean isSet() {
return set;
}
@Override
public int hashCode() {
return Objects.hashCode(set);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BooleanOperator) {
BooleanOperator that = (BooleanOperator) obj;
return Objects.equal(this.set, that.set);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("set", set)
.toString();
}
}
public enum NumericComparator {
EQUALS, LESS_THAN, GREATER_THAN
}
/**
* Searches numeric values.
*/
public static class NumericOperator implements Operator {
private static final long serialVersionUID = 1L;
private final long value;
private final NumericComparator type;
private NumericOperator(long value, NumericComparator type) {
super();
this.value = value;
this.type = type;
}
/**
* Gets the operation type
*
* @return not null
*/
public NumericComparator getType() {
return type;
}
/**
* Gets the value to be compared.
*
* @return the value
*/
public long getValue() {
return value;
}
@Override
public int hashCode() {
return Objects.hashCode(value, type);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof NumericOperator) {
NumericOperator that = (NumericOperator) obj;
return Objects.equal(this.value, that.value)
&& Objects.equal(this.type, that.type);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("value", value)
.add("type", type)
.toString();
}
}
public enum DateComparator {
BEFORE, AFTER, ON
}
/**
* Operates on a date.
*/
public static class DateOperator implements HeaderOperator {
private static final long serialVersionUID = 1L;
public static final int BEFORE = 1;
public static final int AFTER = 2;
public static final int ON = 3;
private final DateComparator type;
private final Date date;
private final DateResolution dateResolution;
public DateOperator(DateComparator type, Date date, DateResolution dateResolution) {
Preconditions.checkNotNull(date);
Preconditions.checkNotNull(dateResolution);
this.type = type;
this.date = date;
this.dateResolution = dateResolution;
}
public Date getDate() {
return date;
}
public DateResolution getDateResultion() {
return dateResolution;
}
/**
* Gets the operator type.
*
* @return the type, either {@link DateComparator#BEFORE},
* {@link DateComparator#AFTER} or {@link DateComparator#ON}
*/
public DateComparator getType() {
return type;
}
@Override
public int hashCode() {
return Objects.hashCode(dateResolution, type);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DateOperator) {
DateOperator that = (DateOperator) obj;
return Objects.equal(this.dateResolution, that.dateResolution)
&& Objects.equal(this.type, that.type);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("date", date)
.add("dateResolution", dateResolution)
.add("type", type)
.toString();
}
}
/**
* Search for numbers within set of ranges.
*/
public static class InOperator implements Operator {
private static final long serialVersionUID = 1L;
private final NumericRange[] range;
public InOperator(NumericRange[] range) {
super();
this.range = range;
}
/**
* Gets the filtering ranges. Values falling within these ranges will be
* selected.
*
* @return the NumericRange
's search on, not null
*/
public NumericRange[] getRange() {
return range;
}
@Override
public int hashCode() {
return range.length;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final InOperator other = (InOperator) obj;
if (!Arrays.equals(range, other.range)) {
return false;
}
return true;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("range", Arrays.toString(range))
.toString();
}
}
/**
* Search for uids within set of ranges.
*/
public static class UidInOperator implements Operator {
private static final long serialVersionUID = 1L;
private final UidRange[] ranges;
public UidInOperator(UidRange[] ranges) {
super();
this.ranges = ranges;
}
/**
* Gets the filtering ranges. Values falling within these ranges will be
* selected.
*
* @return the UidRange
's search on, not null
*/
public UidRange[] getRange() {
return ranges;
}
@Override
public int hashCode() {
return Arrays.hashCode(ranges);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof UidInOperator) {
UidInOperator other = (UidInOperator) obj;
return Arrays.equals(this.ranges, other.ranges);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("ranges", Arrays.toString(ranges))
.toString();
}
}
}