Package org.apache.james.mailbox.store.search

Source Code of org.apache.james.mailbox.store.search.MessageSearches

/****************************************************************
* 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.store.search;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;

import javax.mail.Flags;

import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.exception.UnsupportedSearchException;
import org.apache.james.mailbox.model.MessageResult.Header;
import org.apache.james.mailbox.model.SearchQuery;
import org.apache.james.mailbox.model.SearchQuery.AddressType;
import org.apache.james.mailbox.model.SearchQuery.DateResolution;
import org.apache.james.mailbox.model.SearchQuery.NumericRange;
import org.apache.james.mailbox.store.ResultUtils;
import org.apache.james.mailbox.store.mail.model.Message;
import org.apache.james.mailbox.store.search.comparator.CombinedComparator;
import org.apache.james.mime4j.MimeException;
import org.apache.james.mime4j.dom.address.Address;
import org.apache.james.mime4j.dom.address.AddressList;
import org.apache.james.mime4j.dom.address.Group;
import org.apache.james.mime4j.dom.address.Mailbox;
import org.apache.james.mime4j.dom.address.MailboxList;
import org.apache.james.mime4j.dom.datetime.DateTime;
import org.apache.james.mime4j.field.address.AddressFormatter;
import org.apache.james.mime4j.field.address.LenientAddressBuilder;
import org.apache.james.mime4j.field.datetime.parser.DateTimeParser;
import org.apache.james.mime4j.field.datetime.parser.ParseException;
import org.slf4j.Logger;

/**
* Utility methods to help perform search operations.
*/
public class MessageSearches implements Iterable<Long> {

    private Iterator<Message<?>> messages;
    private SearchQuery query;
    private Logger log;

    public MessageSearches(Iterator<Message<?>> messages, SearchQuery query) {
        this(messages, query, null);
    }

    public MessageSearches(Iterator<Message<?>> messages, SearchQuery query, Logger log) {
        this.messages = messages;
        this.query = query;
        this.log = log;
    }

    /**
     * Empty constructor only for tests (which test isMatch())
     */
    public MessageSearches() {
    }

    private Collection<Long> search() {
        TreeSet<Message<?>> matched = new TreeSet<Message<?>>(CombinedComparator.create(query.getSorts()));
        while (messages.hasNext()) {
            Message<?> m = messages.next();
            try {
                if (isMatch(query, m, log)) {
                    matched.add(m);
                }
            } catch (MailboxException e) {
                log.debug("Unable to search message " + m.getUid(), e);
            }
        }
        Set<Long> uids = new HashSet<Long>();
        Iterator<Message<?>> matchedIt = matched.iterator();
        while (matchedIt.hasNext()) {
            uids.add(matchedIt.next().getUid());
        }
        return uids;
    }

    /**
     * Does the row match the given criteria?
     *
     * @param query
     *            <code>SearchQuery</code>, not null
     * @param message
     *            <code>Message</code>, not null
     * @param log
     *            the logger to use
     * @return <code>true</code> if the row matches the given criteria,
     *         <code>false</code> otherwise
     * @throws MailboxException
     */
    protected boolean isMatch(final SearchQuery query, final Message<?> message, Logger log) throws MailboxException {
        final List<SearchQuery.Criterion> criteria = query.getCriterias();
        final Collection<Long> recentMessageUids = query.getRecentMessageUids();
        boolean result = true;
        if (criteria != null) {
            for (Iterator<SearchQuery.Criterion> it = criteria.iterator(); it.hasNext();) {
                final SearchQuery.Criterion criterion = it.next();
                if (!isMatch(criterion, message, recentMessageUids, log)) {
                    result = false;
                    break;
                }
            }
        }
        return result;
    }

    /**
     * Does the row match the given criterion?
     *
     * @param criterion
     *            the criterion to use
     * @param message
     *            <code>Message</code>, not null
     * @param recentMessageUids
     *            collection of recent message uids
     * @param log
     *            the logger to use
     * @return <code>true</code> if the row matches the given criterion,
     *         <code>false</code> otherwise
     * @throws MailboxException
     */
    public boolean isMatch(SearchQuery.Criterion criterion, Message<?> message,
            final Collection<Long> recentMessageUids, Logger log) throws MailboxException {
        final boolean result;
        if (criterion instanceof SearchQuery.InternalDateCriterion) {
            result = matches((SearchQuery.InternalDateCriterion) criterion, message);
        } else if (criterion instanceof SearchQuery.SizeCriterion) {
            result = matches((SearchQuery.SizeCriterion) criterion, message);
        } else if (criterion instanceof SearchQuery.HeaderCriterion) {
            try {
                result = matches((SearchQuery.HeaderCriterion) criterion, message, log);
            } catch (IOException e) {
                throw new MailboxException("Unable to search header", e);
            }
        } else if (criterion instanceof SearchQuery.UidCriterion) {
            result = matches((SearchQuery.UidCriterion) criterion, message);
        } else if (criterion instanceof SearchQuery.FlagCriterion) {
            result = matches((SearchQuery.FlagCriterion) criterion, message, recentMessageUids);
        } else if (criterion instanceof SearchQuery.CustomFlagCriterion) {
            result = matches((SearchQuery.CustomFlagCriterion) criterion, message, recentMessageUids);
        } else if (criterion instanceof SearchQuery.TextCriterion) {
            result = matches((SearchQuery.TextCriterion) criterion, message, log);
        } else if (criterion instanceof SearchQuery.AllCriterion) {
            result = true;
        } else if (criterion instanceof SearchQuery.ConjunctionCriterion) {
            result = matches((SearchQuery.ConjunctionCriterion) criterion, message, recentMessageUids, log);
        } else if (criterion instanceof SearchQuery.ModSeqCriterion) {
            result = matches((SearchQuery.ModSeqCriterion) criterion, message);
        } else {
            throw new UnsupportedSearchException();
        }
        return result;
    }

    protected boolean matches(SearchQuery.TextCriterion criterion, Message<?> message, Logger log)
            throws MailboxException {
        try {
            final SearchQuery.ContainsOperator operator = criterion.getOperator();
            final String value = operator.getValue();
            switch (criterion.getType()) {
            case BODY:
                return bodyContains(value, message, log);
            case FULL:
                return messageContains(value, message, log);
            default:
                throw new UnsupportedSearchException();
            }
        } catch (IOException e) {
            throw new MailboxException("Unable to parse message", e);
        } catch (MimeException e) {
            throw new MailboxException("Unable to parse message", e);
        }
    }

    protected boolean bodyContains(String value, Message<?> message, Logger log) throws IOException, MimeException {
        final InputStream input = message.getFullContent();
        final boolean result = isInMessage(value, input, false, log);
        return result;
    }

    protected boolean isInMessage(String value, final InputStream input, boolean header, Logger log)
            throws IOException, MimeException {
        final MessageSearcher searcher = new MessageSearcher(value, true, header);
        if (log != null) {
            searcher.setLogger(log);
        }
        final boolean result = searcher.isFoundIn(input);
        return result;
    }

    protected boolean messageContains(String value, Message<?> message, Logger log) throws IOException, MimeException {
        final InputStream input = message.getFullContent();
        final boolean result = isInMessage(value, input, true, log);
        return result;
    }

    private boolean matches(SearchQuery.ConjunctionCriterion criterion, Message<?> message,
            final Collection<Long> recentMessageUids, Logger log) throws MailboxException {
        final List<SearchQuery.Criterion> criteria = criterion.getCriteria();
        switch (criterion.getType()) {
        case NOR:
            return nor(criteria, message, recentMessageUids, log);
        case OR:
            return or(criteria, message, recentMessageUids, log);
        case AND:
            return and(criteria, message, recentMessageUids, log);
        default:
            return false;
        }
    }

    private boolean and(final List<SearchQuery.Criterion> criteria, final Message<?> message,
            final Collection<Long> recentMessageUids, Logger log) throws MailboxException {
        boolean result = true;
        for (Iterator<SearchQuery.Criterion> it = criteria.iterator(); it.hasNext();) {
            final SearchQuery.Criterion criterion = it.next();
            final boolean matches = isMatch(criterion, message, recentMessageUids, log);
            if (!matches) {
                result = false;
                break;
            }
        }
        return result;
    }

    private boolean or(final List<SearchQuery.Criterion> criteria, final Message<?> message,
            final Collection<Long> recentMessageUids, Logger log) throws MailboxException {
        boolean result = false;
        for (Iterator<SearchQuery.Criterion> it = criteria.iterator(); it.hasNext();) {
            final SearchQuery.Criterion criterion = it.next();
            final boolean matches = isMatch(criterion, message, recentMessageUids, log);
            if (matches) {
                result = true;
                break;
            }
        }
        return result;
    }

    private boolean nor(final List<SearchQuery.Criterion> criteria, final Message<?> message,
            final Collection<Long> recentMessageUids, Logger log) throws MailboxException {
        boolean result = true;
        for (Iterator<SearchQuery.Criterion> it = criteria.iterator(); it.hasNext();) {
            final SearchQuery.Criterion criterion = it.next();
            final boolean matches = isMatch(criterion, message, recentMessageUids, log);
            if (matches) {
                result = false;
                break;
            }
        }
        return result;
    }

    private boolean matches(SearchQuery.FlagCriterion criterion, Message<?> message,
            final Collection<Long> recentMessageUids) {
        final SearchQuery.BooleanOperator operator = criterion.getOperator();
        final boolean isSet = operator.isSet();
        final Flags.Flag flag = criterion.getFlag();
        final boolean result;
        if (flag == Flags.Flag.ANSWERED) {
            result = isSet == message.isAnswered();
        } else if (flag == Flags.Flag.SEEN) {
            result = isSet == message.isSeen();
        } else if (flag == Flags.Flag.DRAFT) {
            result = isSet == message.isDraft();
        } else if (flag == Flags.Flag.FLAGGED) {
            result = isSet == message.isFlagged();
        } else if (flag == Flags.Flag.RECENT) {
            final long uid = message.getUid();
            result = isSet == recentMessageUids.contains(Long.valueOf(uid));
        } else if (flag == Flags.Flag.DELETED) {
            result = isSet == message.isDeleted();
        } else {
            result = false;
        }
        return result;
    }

    private boolean matches(SearchQuery.CustomFlagCriterion criterion, Message<?> message,
            final Collection<Long> recentMessageUids) {
        final SearchQuery.BooleanOperator operator = criterion.getOperator();
        final boolean isSet = operator.isSet();
        final String flag = criterion.getFlag();
        final boolean result = isSet == message.createFlags().contains(flag);
        return result;
    }

    private boolean matches(SearchQuery.UidCriterion criterion, Message<?> message) {
        final SearchQuery.InOperator operator = criterion.getOperator();
        final NumericRange[] ranges = operator.getRange();
        final long uid = message.getUid();
        final int length = ranges.length;
        boolean result = false;
        for (int i = 0; i < length; i++) {
            final NumericRange numericRange = ranges[i];
            if (numericRange.isIn(uid)) {
                result = true;
                break;
            }
        }
        return result;
    }

    private boolean matches(SearchQuery.HeaderCriterion criterion, Message<?> message, Logger log)
            throws MailboxException, IOException {
        final SearchQuery.HeaderOperator operator = criterion.getOperator();
        final String headerName = criterion.getHeaderName();
        final boolean result;
        if (operator instanceof SearchQuery.DateOperator) {
            result = matches((SearchQuery.DateOperator) operator, headerName, message);
        } else if (operator instanceof SearchQuery.ContainsOperator) {
            result = matches((SearchQuery.ContainsOperator) operator, headerName, message);
        } else if (operator instanceof SearchQuery.ExistsOperator) {
            result = exists(headerName, message);
        } else if (operator instanceof SearchQuery.AddressOperator) {
            result = matchesAddress((SearchQuery.AddressOperator) operator, headerName, message, log);
        } else {
            throw new UnsupportedSearchException();
        }
        return result;
    }

    /**
     * Match against a {@link AddressType} header
     *
     * @param operator
     * @param headerName
     * @param message
     * @return containsAddress
     * @throws MailboxException
     * @throws IOException
     */
    private boolean matchesAddress(final SearchQuery.AddressOperator operator, final String headerName,
            final Message<?> message, Logger log) throws MailboxException, IOException {
        final String text = operator.getAddress().toUpperCase(Locale.ENGLISH);
        final List<Header> headers = ResultUtils.createHeaders(message);
        for (Header header : headers) {
            final String name = header.getName();
            if (headerName.equalsIgnoreCase(name)) {
                final String value = header.getValue();
                AddressList aList = LenientAddressBuilder.DEFAULT.parseAddressList(value);
                for (int i = 0; i < aList.size(); i++) {
                    Address address = aList.get(i);
                    if (address instanceof Mailbox) {
                        if (AddressFormatter.DEFAULT.encode((Mailbox) address).toUpperCase(Locale.ENGLISH)
                                .contains(text)) {
                            return true;
                        }
                    } else if (address instanceof Group) {
                        MailboxList mList = ((Group) address).getMailboxes();
                        for (int a = 0; a < mList.size(); a++) {
                            if (AddressFormatter.DEFAULT.encode(mList.get(a)).toUpperCase(Locale.ENGLISH)
                                    .contains(text)) {
                                return true;
                            }
                        }
                    }
                }

                // Also try to match against raw header now
                return value.toUpperCase(Locale.ENGLISH).contains(text);
            }
        }
        return false;
    }

    private boolean exists(String headerName, Message<?> message) throws MailboxException, IOException {
        boolean result = false;
        final List<Header> headers = ResultUtils.createHeaders(message);

        for (Header header : headers) {
            final String name = header.getName();
            if (headerName.equalsIgnoreCase(name)) {
                result = true;
                break;
            }
        }
        return result;
    }

    private boolean matches(final SearchQuery.ContainsOperator operator, final String headerName,
            final Message<?> message) throws MailboxException, IOException {
        final String text = operator.getValue().toUpperCase();
        boolean result = false;
        final List<Header> headers = ResultUtils.createHeaders(message);
        for (Header header : headers) {
            final String name = header.getName();
            if (headerName.equalsIgnoreCase(name)) {
                final String value = header.getValue();
                if (value != null) {
                    if (value.toUpperCase().indexOf(text) > -1) {
                        result = true;
                        break;
                    }
                }
            }
        }
        return result;
    }

    private boolean matches(final SearchQuery.DateOperator operator, final String headerName, final Message<?> message)
            throws MailboxException {

        final Date date = operator.getDate();
        final DateResolution res = operator.getDateResultion();
        try {
            final String value = headerValue(headerName, message);
            if (value == null) {
                return false;
            } else {
                try {
                    final Date isoFieldValue = toISODate(value);
                    final SearchQuery.DateComparator type = operator.getType();
                    switch (type) {
                    case AFTER:
                        return after(isoFieldValue, date, res);
                    case BEFORE:
                        return before(isoFieldValue, date, res);
                    case ON:
                        return on(isoFieldValue, date, res);
                    default:
                        throw new UnsupportedSearchException();
                    }
                } catch (ParseException e) {
                    return false;
                }
            }
        } catch (IOException e) {
            return false;
        }
    }

    private String headerValue(final String headerName, final Message<?> message) throws MailboxException, IOException {
        final List<Header> headers = ResultUtils.createHeaders(message);
        String value = null;
        for (Header header : headers) {
            final String name = header.getName();
            if (headerName.equalsIgnoreCase(name)) {
                value = header.getValue();
                break;
            }
        }
        return value;
    }

    private Date toISODate(String value) throws ParseException {
        final StringReader reader = new StringReader(value);
        final DateTime dateTime = new DateTimeParser(reader).parseAll();
        Calendar cal = getGMT();
        cal.set(dateTime.getYear(), dateTime.getMonth() - 1, dateTime.getDay(), dateTime.getHour(),
                dateTime.getMinute(), dateTime.getSecond());
        return cal.getTime();
    }

    private boolean matches(SearchQuery.SizeCriterion criterion, Message<?> message) throws UnsupportedSearchException {
        final SearchQuery.NumericOperator operator = criterion.getOperator();
        final long size = message.getFullContentOctets();
        final long value = operator.getValue();
        switch (operator.getType()) {
        case LESS_THAN:
            return size < value;
        case GREATER_THAN:
            return size > value;
        case EQUALS:
            return size == value;
        default:
            throw new UnsupportedSearchException();
        }
    }

    private boolean matches(SearchQuery.ModSeqCriterion criterion, Message<?> message)
            throws UnsupportedSearchException {
        final SearchQuery.NumericOperator operator = criterion.getOperator();
        final long modSeq = message.getModSeq();
        final long value = operator.getValue();
        switch (operator.getType()) {
        case LESS_THAN:
            return modSeq < value;
        case GREATER_THAN:
            return modSeq > value;
        case EQUALS:
            return modSeq == value;
        default:
            throw new UnsupportedSearchException();
        }
    }

    private boolean matches(SearchQuery.InternalDateCriterion criterion, Message<?> message)
            throws UnsupportedSearchException {
        final SearchQuery.DateOperator operator = criterion.getOperator();
        final boolean result = matchesInternalDate(operator, message);
        return result;
    }

    private boolean matchesInternalDate(final SearchQuery.DateOperator operator, final Message<?> message)
            throws UnsupportedSearchException {
        final Date date = operator.getDate();
        final DateResolution res = operator.getDateResultion();
        final Date internalDate = message.getInternalDate();
        final SearchQuery.DateComparator type = operator.getType();
        switch (type) {
        case ON:
            return on(internalDate, date, res);
        case BEFORE:
            return before(internalDate, date, res);
        case AFTER:
            return after(internalDate, date, res);
        default:
            throw new UnsupportedSearchException();
        }
    }

    private boolean on(Date date1, final Date date2, DateResolution res) {
        String d1 = createDateString(date1, res);
        String d2 = createDateString(date2, res);
        return d1.compareTo(d2) == 0;
    }

    private boolean before(Date date1, final Date date2, DateResolution res) {
        String d1 = createDateString(date1, res);
        String d2 = createDateString(date2, res);

        return d1.compareTo(d2) < 0;
    }

    private boolean after(Date date1, final Date date2, DateResolution res) {
        String d1 = createDateString(date1, res);
        String d2 = createDateString(date2, res);

        return d1.compareTo(d2) > 0;
    }

    private String createDateString(Date date, DateResolution res) {
        SimpleDateFormat format;
        switch (res) {
        case Year:
            format = new SimpleDateFormat("yyyy");
            break;
        case Month:
            format = new SimpleDateFormat("yyyyMM");
            break;
        case Day:
            format = new SimpleDateFormat("yyyyMMdd");
            break;
        case Hour:
            format = new SimpleDateFormat("yyyyMMddhh");
            break;
        case Minute:
            format = new SimpleDateFormat("yyyyMMddhhmm");
            break;
        case Second:
            format = new SimpleDateFormat("yyyyMMddhhmmss");
            break;
        default:
            format = new SimpleDateFormat("yyyyMMddhhmmssSSS");

            break;
        }
        format.setCalendar(getGMT());
        return format.format(date);
    }

    private Calendar getGMT() {
        return Calendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.ENGLISH);
    }

    /**
     * Return a {@link Iterator} which holds all uids which matched, sorted
     * according to the SearchQuery
     *
     */
    public Iterator<Long> iterator() {
        return search().iterator();
    }

}
TOP

Related Classes of org.apache.james.mailbox.store.search.MessageSearches

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.