//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002 Vincent Richard <vincent@vmime.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 3 of
// the License, or (at your option) any later version.
//
// 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
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// Linking this library statically or dynamically with other modules is making
// a combined work based on this library. Thus, the terms and conditions of
// the GNU General Public License cover the whole combination.
//
#include "vmime/config.hpp"
#if VMIME_HAVE_MESSAGING_FEATURES
#include "vmime/net/messageSet.hpp"
#include <iterator>
#include <algorithm>
#include <typeinfo>
namespace vmime {
namespace net {
// messageSetEnumerator
messageSetEnumerator::~messageSetEnumerator()
{
}
// messageRange
messageRange::messageRange() {
}
messageRange::~messageRange() {
}
// numberMessageRange
numberMessageRange::numberMessageRange(const size_t number)
: m_first(number),
m_last(number) {
if (number < 1) {
throw std::invalid_argument("number");
}
}
numberMessageRange::numberMessageRange(const size_t first, const size_t last)
: m_first(first),
m_last(last) {
if (first < 1 || first == static_cast <size_t>(-1)) {
throw std::invalid_argument("first");
} else if (last != static_cast <size_t>(-1) && last < first) {
throw std::invalid_argument("last");
}
}
numberMessageRange::numberMessageRange(const numberMessageRange& other)
: messageRange(),
m_first(other.m_first),
m_last(other.m_last) {
}
size_t numberMessageRange::getFirst() const {
return m_first;
}
size_t numberMessageRange::getLast() const {
return m_last;
}
void numberMessageRange::enumerate(messageSetEnumerator& en) const {
en.enumerateNumberMessageRange(*this);
}
messageRange* numberMessageRange::clone() const {
return new numberMessageRange(*this);
}
// UIDMessageRange
UIDMessageRange::UIDMessageRange(const message::uid& uid)
: m_first(uid),
m_last(uid) {
}
UIDMessageRange::UIDMessageRange(const message::uid& first, const message::uid& last)
: m_first(first),
m_last(last) {
}
UIDMessageRange::UIDMessageRange(const UIDMessageRange& other)
: messageRange(),
m_first(other.m_first),
m_last(other.m_last) {
}
const message::uid UIDMessageRange::getFirst() const {
return m_first;
}
const message::uid UIDMessageRange::getLast() const {
return m_last;
}
void UIDMessageRange::enumerate(messageSetEnumerator& en) const {
en.enumerateUIDMessageRange(*this);
}
messageRange* UIDMessageRange::clone() const {
return new UIDMessageRange(*this);
}
// messageSet
messageSet::messageSet() {
}
messageSet::messageSet(const messageSet& other)
: object() {
m_ranges.resize(other.m_ranges.size());
for (size_t i = 0, n = other.m_ranges.size() ; i < n ; ++i) {
m_ranges[i] = other.m_ranges[i]->clone();
}
}
messageSet::~messageSet() {
for (size_t i = 0, n = m_ranges.size() ; i < n ; ++i) {
delete m_ranges[i];
}
}
// static
messageSet messageSet::empty() {
return messageSet();
}
// static
messageSet messageSet::byNumber(const size_t number) {
messageSet set;
set.m_ranges.push_back(new numberMessageRange(number));
return set;
}
// static
messageSet messageSet::byNumber(const size_t first, const size_t last) {
messageSet set;
set.m_ranges.push_back(new numberMessageRange(first, last));
return set;
}
// static
messageSet messageSet::byNumber(const std::vector <size_t>& numbers) {
// Sort a copy of the list
std::vector <size_t> sortedNumbers;
sortedNumbers.resize(numbers.size());
std::copy(numbers.begin(), numbers.end(), sortedNumbers.begin());
std::sort(sortedNumbers.begin(), sortedNumbers.end());
// Build the set by detecting ranges of continuous numbers
size_t previous = static_cast <size_t>(-1), rangeStart = static_cast <size_t>(-1);
messageSet set;
for (std::vector <size_t>::const_iterator it = sortedNumbers.begin() ;
it != sortedNumbers.end() ; ++it) {
const size_t current = *it;
if (current == previous) {
continue; // skip duplicates
}
if (current == static_cast <size_t>(-1)) {
throw std::invalid_argument("numbers");
}
if (previous == static_cast <size_t>(-1)) {
previous = current;
rangeStart = current;
} else {
if (current == previous + 1) {
previous = current;
} else {
set.m_ranges.push_back(new numberMessageRange(rangeStart, previous));
previous = current;
rangeStart = current;
}
}
}
set.m_ranges.push_back(new numberMessageRange(rangeStart, previous));
return set;
}
// static
messageSet messageSet::byUID(const message::uid& uid) {
messageSet set;
set.m_ranges.push_back(new UIDMessageRange(uid));
return set;
}
messageSet messageSet::byUID(const message::uid& first, const message::uid& last) {
messageSet set;
set.m_ranges.push_back(new UIDMessageRange(first, last));
return set;
}
messageSet messageSet::byUID(const std::vector <message::uid>& uids) {
std::vector <vmime_uint32> numericUIDs;
for (size_t i = 0, n = uids.size() ; i < n ; ++i) {
const string uid = uids[i];
int numericUID = 0;
const char* p = uid.c_str();
for ( ; *p >= '0' && *p <= '9' ; ++p) {
numericUID = (numericUID * 10) + (*p - '0');
}
if (*p != '\0') {
messageSet set;
// Non-numeric UID, fall back to plain UID list (single-UID ranges)
for (size_t i = 0, n = uids.size() ; i < n ; ++i) {
set.m_ranges.push_back(new UIDMessageRange(uids[i]));
}
return set;
}
numericUIDs.push_back(numericUID);
}
// Sort a copy of the list
std::vector <vmime_uint32> sortedUIDs;
sortedUIDs.resize(numericUIDs.size());
std::copy(numericUIDs.begin(), numericUIDs.end(), sortedUIDs.begin());
std::sort(sortedUIDs.begin(), sortedUIDs.end());
// Build the set by detecting ranges of continuous numbers
vmime_uint32 previous = static_cast <vmime_uint32>(-1), rangeStart = static_cast <vmime_uint32>(-1);
messageSet set;
for (std::vector <vmime_uint32>::const_iterator it = sortedUIDs.begin() ;
it != sortedUIDs.end() ; ++it) {
const vmime_uint32 current = *it;
if (current == previous) {
continue; // skip duplicates
}
if (previous == static_cast <vmime_uint32>(-1)) {
previous = current;
rangeStart = current;
} else {
if (current == previous + 1) {
previous = current;
} else {
set.m_ranges.push_back(
new UIDMessageRange(
utility::stringUtils::toString(rangeStart),
utility::stringUtils::toString(previous)
)
);
previous = current;
rangeStart = current;
}
}
}
set.m_ranges.push_back(
new UIDMessageRange(
utility::stringUtils::toString(rangeStart),
utility::stringUtils::toString(previous)
)
);
return set;
}
void messageSet::addRange(const messageRange& range) {
if (!m_ranges.empty() && typeid(*m_ranges[0]) != typeid(range)) {
throw std::invalid_argument("range");
}
m_ranges.push_back(range.clone());
}
void messageSet::enumerate(messageSetEnumerator& en) const {
for (size_t i = 0, n = m_ranges.size() ; i < n ; ++i) {
m_ranges[i]->enumerate(en);
}
}
bool messageSet::isEmpty() const {
return m_ranges.empty();
}
bool messageSet::isNumberSet() const {
return !isEmpty() && dynamic_cast <numberMessageRange*>(m_ranges[0]) != NULL;
}
bool messageSet::isUIDSet() const {
return !isEmpty() && dynamic_cast <UIDMessageRange*>(m_ranges[0]) != NULL;
}
size_t messageSet::getRangeCount() const {
return m_ranges.size();
}
const messageRange& messageSet::getRangeAt(const size_t i) const {
return *m_ranges[i];
}
} // net
} // vmime
#endif // VMIME_HAVE_MESSAGING_FEATURES