<commit_before>//@author A0097630B
#include "stdafx.h"
#include "You-QueryEngine/task_model.h"

#include "exception.h"
#include "query_parser.h"

using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;

namespace You {
namespace NLP {
namespace UnitTests {

using boost::gregorian::date;
using boost::posix_time::ptime;
using boost::posix_time::hours;
using You::NLP::ParserException;

using You::NLP::QueryParser;
using You::NLP::ADD_QUERY;
using You::NLP::EDIT_QUERY;
using You::NLP::QUERY;

TEST_CLASS(QueryParserTests) {
public:
	TEST_METHOD(throwsExceptionOnEmptyString) {
		Assert::ExpectException<ParserException>(
			std::bind(static_cast<QUERY (*)(const std::wstring&)>(
				&QueryParser::parse), L""));
	}

	TEST_METHOD(throwsExceptionWhenParseFails) {
		// "throw" is currently not defined, so this should work.
		Assert::ExpectException<ParserException>(
			std::bind(static_cast<QUERY (*)(const std::wstring&)>(
				&QueryParser::parse), L"/throw"));
	}

	TEST_METHOD(parsesStringAsTask) {
		QUERY q = QueryParser::parse(L"win");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win"
		}), q);

		q = QueryParser::parse(L"win lottery");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win lottery"
		}), q);
	}

	TEST_METHOD(parsesStringWithDeadlineAsTask) {
		QUERY q = QueryParser::parse(L"win by may 2014");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win",
			TaskPriority::NORMAL,
			ptime(date(2014, boost::gregorian::May, 1), hours(0))
		}), q);

		q = QueryParser::parse(L"win lottery by dec 2014");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win lottery",
			TaskPriority::NORMAL,
			ptime(date(2014, boost::gregorian::Dec, 1), hours(0))
		}), q);
	}

	TEST_METHOD(parsesStringWithPriorityAsTask) {
		QUERY q = QueryParser::parse(L"win!");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win",
			TaskPriority::HIGH
		}), q);

		q = QueryParser::parse(L"win lottery! by dec 2014");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win lottery",
			TaskPriority::HIGH,
			ptime(date(2014, boost::gregorian::Dec, 1), hours(0))
		}), q);
	}

	TEST_METHOD(parsesStringWithSubtasksAsTask) {
		QUERY q = QueryParser::parse(L"Walk the dog by 20 oct : Eat breakfast "
			L"; Take out the dog; Open the door; Buy a new collar by 12 oct");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"Walk the dog",
			TaskPriority::NORMAL,
			ptime(date(2015, boost::gregorian::Oct, 20)),
			{
				{ L"Eat breakfast" },
				{ L"Take out the dog" },
				{ L"Open the door" },
				{
					L"Buy a new collar",
					TaskPriority::NORMAL,
					ptime(date(2015, boost::gregorian::Oct, 12))
				}
			}
		}), q);
	}

	TEST_METHOD(parsesStringWithDependenciesAsTask) {
		QUERY q = QueryParser::parse(L"Buy her flower by 14 dec -> Ask her "
			L"out by 15 dec -> Confess to her by 16 dec");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"Buy her flower",
			TaskPriority::NORMAL,
			ptime(date(2014, boost::gregorian::Dec, 14)),
			{},
			std::shared_ptr<ADD_QUERY>(new ADD_QUERY {
				L"Ask her out",
				TaskPriority::NORMAL,
				ptime(date(2014, boost::gregorian::Dec, 15)),
				{},
				std::shared_ptr<ADD_QUERY>(new ADD_QUERY {
					L"Confess to her",
					TaskPriority::NORMAL,
					ptime(date(2014, boost::gregorian::Dec, 16))
				})
			})
		}), q);
	}

	TEST_METHOD(parsesStringWithDependenciesAndSubtasksAsTask) {
		QUERY q = QueryParser::parse(L"Walk the dog by 20 oct : Eat breakfast "
			L"; Take out the dog; Buy her flower by 14 dec -> Ask her "
			L"out by 15 dec -> Confess to her by 16 dec; Buy a new collar by "
			L"12 oct");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"Walk the dog",
			TaskPriority::NORMAL,
			ptime(date(2015, boost::gregorian::Oct, 20)),
			{
				{ L"Eat breakfast" },
				{ L"Take out the dog" },
				{
					L"Buy her flower",
					TaskPriority::NORMAL,
					ptime(date(2014, boost::gregorian::Dec, 14)),
					{},
					std::shared_ptr<ADD_QUERY>(new ADD_QUERY {
						L"Ask her out",
						TaskPriority::NORMAL,
						ptime(date(2014, boost::gregorian::Dec, 15)),
						{},
						std::shared_ptr<ADD_QUERY>(new ADD_QUERY {
							L"Confess to her",
							TaskPriority::NORMAL,
							ptime(date(2014, boost::gregorian::Dec, 16))
						})
					})
				},
				{
					L"Buy a new collar",
					TaskPriority::NORMAL,
					ptime(date(2015, boost::gregorian::Oct, 12))
				}
			}
		}), q);
	}

	TEST_METHOD(parsesIrregularSpacingAddTask) {
		QUERY q;
		Assert::ExpectException<ParseErrorException>(
			boost::phoenix::bind(&QueryParser::parse,
				L"/adds Hello World by 20 oct"));

		q = QueryParser::parse(L"/add E by 22 oct");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"E",
			TaskPriority::NORMAL,
			ptime(date(2015, boost::gregorian::Oct, 22), hours(0))
		}), q);
	}

	TEST_METHOD(parsesShowQuery) {
		// Boundary case: no filters nor sorts.
		QUERY q = QueryParser::parse(L"/show");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{}, {}
		}), q);

		// Boundary case: one filter, zero sort.
		q = QueryParser::parse(L"/show description='\\\\\\'meh'");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{
				{
					TaskField::DESCRIPTION,
					SHOW_QUERY::Predicate::EQ,
					std::wstring(L"\\\'meh")
				}
			},
			{}
		}), q);

		// Boundary case: more than one filter, zero sort.
		q = QueryParser::parse(L"/show description!='\\\\\\'meh', "
			L"priority<high");

		Assert::AreEqual(QUERY(SHOW_QUERY {
				{
					{
						TaskField::DESCRIPTION,
						SHOW_QUERY::Predicate::NOT_EQ,
						std::wstring(L"\\\'meh")
					},
					{
						TaskField::PRIORITY,
						SHOW_QUERY::Predicate::LESS_THAN,
						TaskPriority::HIGH
					}
				},
				{}
		}), q);

		// Boundary case: zero filter, one sort.
		q = QueryParser::parse(L"/show order by description ascending");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{},
			{ { TaskField::DESCRIPTION, SHOW_QUERY::Order::ASCENDING } }
		}), q);

		// Boundary case: zero filter, more than one sort.
		q = QueryParser::parse(L"/show order by description descending, "
			L"priority");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{},
			{
				{ TaskField::DESCRIPTION, SHOW_QUERY::Order::DESCENDING },
				{ TaskField::PRIORITY, SHOW_QUERY::Order::ASCENDING }
			}
		}), q);

		// Boundary case: nonzero filter, nonzero sort.
		q = QueryParser::parse(L"/show description!='\\\\\\'meh', "
			L"priority<high, priority>normal, deadline>='3 oct', "
			L"deadline<='7 oct', complete=true "
			L"order by description descending, priority");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{
				{
					TaskField::DESCRIPTION,
					SHOW_QUERY::Predicate::NOT_EQ,
					std::wstring(L"\\\'meh")
				},
				{
					TaskField::PRIORITY,
					SHOW_QUERY::Predicate::LESS_THAN,
					TaskPriority::HIGH
				},
				{
					TaskField::PRIORITY,
					SHOW_QUERY::Predicate::GREATER_THAN,
					TaskPriority::NORMAL
				},
				{
					TaskField::DEADLINE,
					SHOW_QUERY::Predicate::GREATER_THAN_EQ,
					boost::posix_time::ptime(
						boost::gregorian::date(2015, 10, 3),
						boost::posix_time::hours(0))
				},
				{
					TaskField::DEADLINE,
					SHOW_QUERY::Predicate::LESS_THAN_EQ,
					boost::posix_time::ptime(
						boost::gregorian::date(2015, 10, 7),
						boost::posix_time::hours(0))
				},
				{
					TaskField::COMPLETE,
					SHOW_QUERY::Predicate::EQ,
					true
				}
			},
			{
				{ TaskField::DESCRIPTION, SHOW_QUERY::Order::DESCENDING },
				{ TaskField::PRIORITY, SHOW_QUERY::Order::ASCENDING }
			}
		}), q);
	}

	TEST_METHOD(parsesShowQueryWithWrongType) {
		Assert::ExpectException<ParserTypeException>([]() {
			QueryParser::parse(L"/show description=false");
		});
	}

	TEST_METHOD(parsesEditQuery) {
		QUERY q = QueryParser::parse(L"/edit 10 set description='meh'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			L"meh"
		}), q);

		q = QueryParser::parse(L"/edit 10 set description='meh with spaces'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			L"meh with spaces"
		}), q);

		q = QueryParser::parse(L"/edit 10 set deadline='oct 2014'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			boost::none,
			boost::none,
			ptime(date(2014, boost::gregorian::Oct, 1), hours(0))
		}), q);

		q = QueryParser::parse(L"/edit 10 set complete");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			boost::none,
			boost::none,
			boost::none,
			true
		}), q);

		q = QueryParser::parse(L"/edit 10 set priority=high");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			boost::none,
			TaskPriority::HIGH
		}), q);
	}

	TEST_METHOD(parsesEditHighPriorityQuery) {
		QUERY q = QueryParser::parse(L"/edit 0!");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			0,
			boost::none,
			TaskPriority::HIGH
		}), q);
	}

	TEST_METHOD(parsesEditDeadlineQuery) {
		QUERY q = QueryParser::parse(L"/edit 0 by 13-Dec-14");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			0,
			boost::none,
			boost::none,
			boost::posix_time::ptime(
				boost::gregorian::date(2014, boost::gregorian::Dec, 13))
		}), q);
	}

	TEST_METHOD(parsesEditSubtaskQuery) {
		QUERY q = QueryParser::parse(L"/edit 0:1");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			0,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			1
		}), q);
	}

	TEST_METHOD(parsesEditDependentQuery) {
		QUERY q = QueryParser::parse(L"/edit 0->1");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			0,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			1
		}), q);
	}

	TEST_METHOD(parsesEditAttachmentQuery) {
		QUERY q = QueryParser::parse(L"/edit 10 attach 'lol'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			{ { true, L"lol" } }
		}), q);

		q = QueryParser::parse(L"/edit 10 detach 'lol'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			{ { false, L"lol" } }
		}), q);
	}

	TEST_METHOD(parsesEditQueryWithWrongType) {
		Assert::ExpectException<ParserTypeException>(
			std::bind(static_cast<QUERY (*)(const std::wstring&)>(
				&QueryParser::parse),
				L"/edit 10 set description='14 oct'"));
	}

	TEST_METHOD(parsesDeleteQuery) {
		QUERY q = QueryParser::parse(L"/delete 10");

		Assert::AreEqual(QUERY(DELETE_QUERY {
			10
		}), q);

		Assert::ExpectException<ParseErrorException>(std::bind(
			static_cast<QUERY (*)(const std::wstring&)>(
				&QueryParser::parse), L"/delete10"));
	}

	TEST_METHOD(parsesUndoQuery) {
		QUERY q = QueryParser::parse(L"/undo");

		Assert::AreEqual(QUERY(UNDO_QUERY {}), q);
	}
};

}  // namespace UnitTests
}  // namespace NLP
}  // namespace You
<commit_msg>Fix wrong date syntax<commit_after>//@author A0097630B
#include "stdafx.h"
#include "You-QueryEngine/task_model.h"

#include "exception.h"
#include "query_parser.h"

using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;

namespace You {
namespace NLP {
namespace UnitTests {

using boost::gregorian::date;
using boost::posix_time::ptime;
using boost::posix_time::hours;
using You::NLP::ParserException;

using You::NLP::QueryParser;
using You::NLP::ADD_QUERY;
using You::NLP::EDIT_QUERY;
using You::NLP::QUERY;

TEST_CLASS(QueryParserTests) {
public:
	TEST_METHOD(throwsExceptionOnEmptyString) {
		Assert::ExpectException<ParserException>(
			std::bind(static_cast<QUERY (*)(const std::wstring&)>(
				&QueryParser::parse), L""));
	}

	TEST_METHOD(throwsExceptionWhenParseFails) {
		// "throw" is currently not defined, so this should work.
		Assert::ExpectException<ParserException>(
			std::bind(static_cast<QUERY (*)(const std::wstring&)>(
				&QueryParser::parse), L"/throw"));
	}

	TEST_METHOD(parsesStringAsTask) {
		QUERY q = QueryParser::parse(L"win");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win"
		}), q);

		q = QueryParser::parse(L"win lottery");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win lottery"
		}), q);
	}

	TEST_METHOD(parsesStringWithDeadlineAsTask) {
		QUERY q = QueryParser::parse(L"win by may 2014");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win",
			TaskPriority::NORMAL,
			ptime(date(2014, boost::gregorian::May, 1), hours(0))
		}), q);

		q = QueryParser::parse(L"win lottery by dec 2014");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win lottery",
			TaskPriority::NORMAL,
			ptime(date(2014, boost::gregorian::Dec, 1), hours(0))
		}), q);
	}

	TEST_METHOD(parsesStringWithPriorityAsTask) {
		QUERY q = QueryParser::parse(L"win!");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win",
			TaskPriority::HIGH
		}), q);

		q = QueryParser::parse(L"win lottery! by dec 2014");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win lottery",
			TaskPriority::HIGH,
			ptime(date(2014, boost::gregorian::Dec, 1), hours(0))
		}), q);
	}

	TEST_METHOD(parsesStringWithSubtasksAsTask) {
		QUERY q = QueryParser::parse(L"Walk the dog by 20 oct : Eat breakfast "
			L"; Take out the dog; Open the door; Buy a new collar by 12 oct");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"Walk the dog",
			TaskPriority::NORMAL,
			ptime(date(2015, boost::gregorian::Oct, 20)),
			{
				{ L"Eat breakfast" },
				{ L"Take out the dog" },
				{ L"Open the door" },
				{
					L"Buy a new collar",
					TaskPriority::NORMAL,
					ptime(date(2015, boost::gregorian::Oct, 12))
				}
			}
		}), q);
	}

	TEST_METHOD(parsesStringWithDependenciesAsTask) {
		QUERY q = QueryParser::parse(L"Buy her flower by 14 dec -> Ask her "
			L"out by 15 dec -> Confess to her by 16 dec");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"Buy her flower",
			TaskPriority::NORMAL,
			ptime(date(2014, boost::gregorian::Dec, 14)),
			{},
			std::shared_ptr<ADD_QUERY>(new ADD_QUERY {
				L"Ask her out",
				TaskPriority::NORMAL,
				ptime(date(2014, boost::gregorian::Dec, 15)),
				{},
				std::shared_ptr<ADD_QUERY>(new ADD_QUERY {
					L"Confess to her",
					TaskPriority::NORMAL,
					ptime(date(2014, boost::gregorian::Dec, 16))
				})
			})
		}), q);
	}

	TEST_METHOD(parsesStringWithDependenciesAndSubtasksAsTask) {
		QUERY q = QueryParser::parse(L"Walk the dog by 20 oct : Eat breakfast "
			L"; Take out the dog; Buy her flower by 14 dec -> Ask her "
			L"out by 15 dec -> Confess to her by 16 dec; Buy a new collar by "
			L"12 oct");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"Walk the dog",
			TaskPriority::NORMAL,
			ptime(date(2015, boost::gregorian::Oct, 20)),
			{
				{ L"Eat breakfast" },
				{ L"Take out the dog" },
				{
					L"Buy her flower",
					TaskPriority::NORMAL,
					ptime(date(2014, boost::gregorian::Dec, 14)),
					{},
					std::shared_ptr<ADD_QUERY>(new ADD_QUERY {
						L"Ask her out",
						TaskPriority::NORMAL,
						ptime(date(2014, boost::gregorian::Dec, 15)),
						{},
						std::shared_ptr<ADD_QUERY>(new ADD_QUERY {
							L"Confess to her",
							TaskPriority::NORMAL,
							ptime(date(2014, boost::gregorian::Dec, 16))
						})
					})
				},
				{
					L"Buy a new collar",
					TaskPriority::NORMAL,
					ptime(date(2015, boost::gregorian::Oct, 12))
				}
			}
		}), q);
	}

	TEST_METHOD(parsesIrregularSpacingAddTask) {
		QUERY q;
		Assert::ExpectException<ParseErrorException>(
			boost::phoenix::bind(&QueryParser::parse,
				L"/adds Hello World by 20 oct"));

		q = QueryParser::parse(L"/add E by 22 oct");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"E",
			TaskPriority::NORMAL,
			ptime(date(2015, boost::gregorian::Oct, 22), hours(0))
		}), q);
	}

	TEST_METHOD(parsesShowQuery) {
		// Boundary case: no filters nor sorts.
		QUERY q = QueryParser::parse(L"/show");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{}, {}
		}), q);

		// Boundary case: one filter, zero sort.
		q = QueryParser::parse(L"/show description='\\\\\\'meh'");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{
				{
					TaskField::DESCRIPTION,
					SHOW_QUERY::Predicate::EQ,
					std::wstring(L"\\\'meh")
				}
			},
			{}
		}), q);

		// Boundary case: more than one filter, zero sort.
		q = QueryParser::parse(L"/show description!='\\\\\\'meh', "
			L"priority<high");

		Assert::AreEqual(QUERY(SHOW_QUERY {
				{
					{
						TaskField::DESCRIPTION,
						SHOW_QUERY::Predicate::NOT_EQ,
						std::wstring(L"\\\'meh")
					},
					{
						TaskField::PRIORITY,
						SHOW_QUERY::Predicate::LESS_THAN,
						TaskPriority::HIGH
					}
				},
				{}
		}), q);

		// Boundary case: zero filter, one sort.
		q = QueryParser::parse(L"/show order by description ascending");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{},
			{ { TaskField::DESCRIPTION, SHOW_QUERY::Order::ASCENDING } }
		}), q);

		// Boundary case: zero filter, more than one sort.
		q = QueryParser::parse(L"/show order by description descending, "
			L"priority");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{},
			{
				{ TaskField::DESCRIPTION, SHOW_QUERY::Order::DESCENDING },
				{ TaskField::PRIORITY, SHOW_QUERY::Order::ASCENDING }
			}
		}), q);

		// Boundary case: nonzero filter, nonzero sort.
		q = QueryParser::parse(L"/show description!='\\\\\\'meh', "
			L"priority<high, priority>normal, deadline>='3 oct', "
			L"deadline<='7 oct', complete=true "
			L"order by description descending, priority");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{
				{
					TaskField::DESCRIPTION,
					SHOW_QUERY::Predicate::NOT_EQ,
					std::wstring(L"\\\'meh")
				},
				{
					TaskField::PRIORITY,
					SHOW_QUERY::Predicate::LESS_THAN,
					TaskPriority::HIGH
				},
				{
					TaskField::PRIORITY,
					SHOW_QUERY::Predicate::GREATER_THAN,
					TaskPriority::NORMAL
				},
				{
					TaskField::DEADLINE,
					SHOW_QUERY::Predicate::GREATER_THAN_EQ,
					boost::posix_time::ptime(
						boost::gregorian::date(2015, 10, 3),
						boost::posix_time::hours(0))
				},
				{
					TaskField::DEADLINE,
					SHOW_QUERY::Predicate::LESS_THAN_EQ,
					boost::posix_time::ptime(
						boost::gregorian::date(2015, 10, 7),
						boost::posix_time::hours(0))
				},
				{
					TaskField::COMPLETE,
					SHOW_QUERY::Predicate::EQ,
					true
				}
			},
			{
				{ TaskField::DESCRIPTION, SHOW_QUERY::Order::DESCENDING },
				{ TaskField::PRIORITY, SHOW_QUERY::Order::ASCENDING }
			}
		}), q);
	}

	TEST_METHOD(parsesShowQueryWithWrongType) {
		Assert::ExpectException<ParserTypeException>([]() {
			QueryParser::parse(L"/show description=false");
		});
	}

	TEST_METHOD(parsesEditQuery) {
		QUERY q = QueryParser::parse(L"/edit 10 set description='meh'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			L"meh"
		}), q);

		q = QueryParser::parse(L"/edit 10 set description='meh with spaces'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			L"meh with spaces"
		}), q);

		q = QueryParser::parse(L"/edit 10 set deadline='oct 2014'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			boost::none,
			boost::none,
			ptime(date(2014, boost::gregorian::Oct, 1), hours(0))
		}), q);

		q = QueryParser::parse(L"/edit 10 set complete");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			boost::none,
			boost::none,
			boost::none,
			true
		}), q);

		q = QueryParser::parse(L"/edit 10 set priority=high");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			boost::none,
			TaskPriority::HIGH
		}), q);
	}

	TEST_METHOD(parsesEditHighPriorityQuery) {
		QUERY q = QueryParser::parse(L"/edit 0!");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			0,
			boost::none,
			TaskPriority::HIGH
		}), q);
	}

	TEST_METHOD(parsesEditDeadlineQuery) {
		QUERY q = QueryParser::parse(L"/edit 0 by 2014-Dec-13");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			0,
			boost::none,
			boost::none,
			boost::posix_time::ptime(
				boost::gregorian::date(2014, boost::gregorian::Dec, 13))
		}), q);
	}

	TEST_METHOD(parsesEditSubtaskQuery) {
		QUERY q = QueryParser::parse(L"/edit 0:1");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			0,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			1
		}), q);
	}

	TEST_METHOD(parsesEditDependentQuery) {
		QUERY q = QueryParser::parse(L"/edit 0->1");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			0,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			1
		}), q);
	}

	TEST_METHOD(parsesEditAttachmentQuery) {
		QUERY q = QueryParser::parse(L"/edit 10 attach 'lol'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			{ { true, L"lol" } }
		}), q);

		q = QueryParser::parse(L"/edit 10 detach 'lol'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			boost::none,
			{ { false, L"lol" } }
		}), q);
	}

	TEST_METHOD(parsesEditQueryWithWrongType) {
		Assert::ExpectException<ParserTypeException>(
			std::bind(static_cast<QUERY (*)(const std::wstring&)>(
				&QueryParser::parse),
				L"/edit 10 set description='14 oct'"));
	}

	TEST_METHOD(parsesDeleteQuery) {
		QUERY q = QueryParser::parse(L"/delete 10");

		Assert::AreEqual(QUERY(DELETE_QUERY {
			10
		}), q);

		Assert::ExpectException<ParseErrorException>(std::bind(
			static_cast<QUERY (*)(const std::wstring&)>(
				&QueryParser::parse), L"/delete10"));
	}

	TEST_METHOD(parsesUndoQuery) {
		QUERY q = QueryParser::parse(L"/undo");

		Assert::AreEqual(QUERY(UNDO_QUERY {}), q);
	}
};

}  // namespace UnitTests
}  // namespace NLP
}  // namespace You
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
**     the names of its contributors may be used to endorse or promote
**     products derived from this software without specific prior written
**     permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
** $QT_END_LICENSE$
**
****************************************************************************/

#include "contacteditor.h"

#include <QtGui>

const int MAX_AVATAR_DISPLAY_SIZE = 120;

ContactEditor::ContactEditor(QWidget *parent)
        :QWidget(parent)
{
    m_manager = 0;
    m_contactId = QContactLocalId(0);

    m_nameEdit = new QLineEdit(this);
    m_phoneEdit = new QLineEdit(this);
    m_emailEdit = new QLineEdit(this);
    m_addrEdit = new QLineEdit(this);
    m_avatarBtn = new QPushButton(tr("Set picture"), this);
    m_clearAvatarBtn = new QPushButton(tr("Clear"), this);
    m_avatarView = new QLabel(this);
    connect(m_avatarBtn, SIGNAL(clicked()), this, SLOT(avatarClicked()));
    connect(m_clearAvatarBtn, SIGNAL(clicked()), this, SLOT(clearAvatarClicked()));

    QFormLayout *detailsLayout = new QFormLayout;
    QLabel *nameLabel = new QLabel(tr("Name"), this);
    QLabel *phoneLabel = new QLabel(tr("Phone"), this);
    QLabel *emailLabel = new QLabel(tr("Email"), this);
    QLabel *addressLabel = new QLabel(tr("Address"), this);
    QLabel *avatarLabel = new QLabel(tr("Picture"), this);
    QHBoxLayout *avatarBtnLayout = new QHBoxLayout;
    avatarBtnLayout->addWidget(m_avatarBtn);
    avatarBtnLayout->addWidget(m_clearAvatarBtn);
    if (QApplication::desktop()->availableGeometry().width() < 360) {
        // Narrow screen: put label on separate line to textbox
        detailsLayout->addRow(nameLabel);
        detailsLayout->addRow(m_nameEdit);
        detailsLayout->addRow(phoneLabel);
        detailsLayout->addRow(m_phoneEdit);
        detailsLayout->addRow(emailLabel);
        detailsLayout->addRow(m_emailEdit);
        detailsLayout->addRow(addressLabel);
        detailsLayout->addRow(m_addrEdit);
        detailsLayout->addRow(avatarLabel);
        detailsLayout->addRow(avatarBtnLayout);
        detailsLayout->addRow(m_avatarView);
    } else {
        // Wide screen: put label on same line as textbox
        detailsLayout->addRow(nameLabel, m_nameEdit);
        detailsLayout->addRow(phoneLabel, m_phoneEdit);
        detailsLayout->addRow(emailLabel, m_emailEdit);
        detailsLayout->addRow(addressLabel, m_addrEdit);
        detailsLayout->addRow(avatarLabel, avatarBtnLayout);
        detailsLayout->addRow("", m_avatarView);
    }
    detailsLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
    detailsLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);

    QScrollArea *detailsScrollArea = new QScrollArea(this);
    detailsScrollArea->setWidgetResizable(true);
    QWidget *detailsContainer = new QWidget(detailsScrollArea);
    detailsContainer->setLayout(detailsLayout);
    detailsScrollArea->setWidget(detailsContainer);

    QVBoxLayout *editLayout = new QVBoxLayout;
    editLayout->addWidget(detailsScrollArea);

#ifdef Q_OS_SYMBIAN
    // In symbian "save" and "cancel" buttons are softkeys.
    m_saveBtn = new QAction(tr("Save"), this);
    m_saveBtn->setSoftKeyRole(QAction::PositiveSoftKey);
    addAction(m_saveBtn);
    connect(m_saveBtn, SIGNAL(triggered(bool)), this, SLOT(saveClicked()));
    m_cancelBtn = new QAction(tr("Cancel"), this);
    m_cancelBtn->setSoftKeyRole(QAction::NegativeSoftKey);
    addAction(m_cancelBtn);
    connect(m_cancelBtn, SIGNAL(triggered(bool)), this, SLOT(cancelClicked()));
#else
    m_saveBtn = new QPushButton(tr("&Save"), this);
    m_saveBtn->setDefault(true);
    connect(m_saveBtn, SIGNAL(clicked()), this, SLOT(saveClicked()));
    m_cancelBtn = new QPushButton(tr("&Cancel"), this);
    connect(m_cancelBtn, SIGNAL(clicked()), this, SLOT(cancelClicked()));
    QHBoxLayout *btnLayout = new QHBoxLayout;
    btnLayout->addWidget(m_saveBtn);
    btnLayout->addWidget(m_cancelBtn);
    editLayout->addLayout(btnLayout);
#endif

    setLayout(editLayout);
}

ContactEditor::~ContactEditor()
{
}

void ContactEditor::setCurrentContact(QContactManager* manager, QContactLocalId currentId)
{
    m_manager = manager;
    m_contactId = currentId;
    m_newAvatarPath = QString();

    // Clear UI
    m_nameEdit->clear();
    m_phoneEdit->clear();
    m_emailEdit->clear();
    m_addrEdit->clear();

    if (manager == 0) {
        m_saveBtn->setEnabled(false);
        return;
    }

    // enable the UI.
    m_saveBtn->setEnabled(true);

    // otherwise, build from the contact details.
    QContact curr;
    if (m_contactId != QContactLocalId(0))
        curr = manager->contact(m_contactId);

    // Disable fields & buttons according to what the backend supports
    QMap<QString, QContactDetailDefinition> defs = m_manager->detailDefinitions(QContactType::TypeContact);

    // name
    //QContactName nm = curr.detail(QContactName::DefinitionName);
    if (m_contactId != QContactLocalId(0))
        m_nameEdit->setText(manager->synthesizedContactDisplayLabel(curr));

    // phonenumber
    QContactPhoneNumber phn = curr.detail(QContactPhoneNumber::DefinitionName);
    m_phoneEdit->setText(phn.value(QContactPhoneNumber::FieldNumber));

    // email
    if (defs.contains(QContactEmailAddress::DefinitionName)) {
        QContactEmailAddress em = curr.detail(QContactEmailAddress::DefinitionName);
        m_emailEdit->setText(em.value(QContactEmailAddress::FieldEmailAddress));
        m_emailEdit->setReadOnly(false);
    } else {
        m_emailEdit->setText("<not supported>");
        m_emailEdit->setReadOnly(true);
    }

    // address
    if (defs.contains(QContactAddress::DefinitionName)) {
        QContactAddress adr = curr.detail(QContactAddress::DefinitionName);
        m_addrEdit->setText(adr.value(QContactAddress::FieldStreet)); // ugly hack.
        m_addrEdit->setReadOnly(false);
    } else {
        m_addrEdit->setText("<not supported>");
        m_addrEdit->setReadOnly(true);
    }

    // avatar viewer
    if (defs.contains(QContactAvatar::DefinitionName)
        || defs.contains(QContactThumbnail::DefinitionName)) {
        m_avatarBtn->setEnabled(true);
        QContactAvatar av = curr.detail(QContactAvatar::DefinitionName);
        QContactThumbnail thumb = curr.detail(QContactThumbnail::DefinitionName);
        m_avatarView->clear();
        m_newAvatarPath = av.imageUrl().toLocalFile();
        m_thumbnail = thumb.thumbnail();
        if (m_thumbnail.isNull()) {
            if (m_newAvatarPath.isEmpty()) {
                m_avatarView->clear();
                m_clearAvatarBtn->setDisabled(true);
            } else {
                setAvatarPixmap(QPixmap(av.imageUrl().toLocalFile()));
                m_thumbnail = QImage(av.imageUrl().toLocalFile());
            }
        } else {
            setAvatarPixmap(QPixmap::fromImage(m_thumbnail));
        }
    } else {
        m_avatarBtn->setDisabled(true);
        m_clearAvatarBtn->setDisabled(true);
    }
}

QString ContactEditor::nameField()
{
    // return the field which the name data should be saved in.
    if (!m_manager)
        return QString();

    QMap<QString, QContactDetailDefinition> defs = m_manager->detailDefinitions(QContactType::TypeContact);
    QContactDetailDefinition nameDef = defs.value(QContactName::DefinitionName);
    if (nameDef.fields().keys().contains(QContactName::FieldCustomLabel)) {
        return QString(QLatin1String(QContactName::FieldCustomLabel));
    } else if (nameDef.fields().keys().contains(QContactName::FieldFirstName)) {
        return QString(QLatin1String(QContactName::FieldFirstName));
    } else {
        return QString();
    }
}

void ContactEditor::setAvatarPixmap(const QPixmap &pixmap)
{
    if (pixmap.isNull())
        return;
    QPixmap scaled = pixmap.scaled(QSize(MAX_AVATAR_DISPLAY_SIZE, MAX_AVATAR_DISPLAY_SIZE),
                                   Qt::KeepAspectRatio,
                                   Qt::SmoothTransformation);
    m_avatarView->setPixmap(scaled);
    m_avatarView->setMaximumSize(scaled.size());
    m_clearAvatarBtn->setEnabled(true);
}

void ContactEditor::clearAvatarClicked()
{
    m_avatarView->clear();
    m_thumbnail = QImage();
    m_newAvatarPath.clear();
    m_clearAvatarBtn->setDisabled(true);
}

void ContactEditor::avatarClicked()
{
    // put up a file dialog, and update the new avatar path.
    QString fileName = QFileDialog::getOpenFileName(this,
       tr("Select Contact Picture"), ".", tr("Image Files (*.png *.jpg *.bmp)"));

    if (!fileName.isEmpty()) {
        m_newAvatarPath = fileName;
        m_thumbnail = QImage(m_newAvatarPath);
        setAvatarPixmap(QPixmap::fromImage(m_thumbnail));
    }
}

void ContactEditor::saveClicked()
{
    if (!m_manager) {
        qWarning() << "No manager selected; cannot save.";
    } else {
        QContact curr;
        if (m_contactId != QContactLocalId(0))
            curr = m_manager->contact(m_contactId);

        if (m_nameEdit->text().isEmpty()) {
            QMessageBox::information(this, "Failed!", "You must give a name for the contact!");
            return;
        }

        if (m_nameEdit->text() != m_manager->synthesizedContactDisplayLabel(curr)) {
            // if the name has changed (ie, is different to the synthed label) then save it as a custom label.
            QString saveNameField = nameField();
            if (!saveNameField.isEmpty()) {
                QContactName nm = curr.detail(QContactName::DefinitionName);
                nm.setValue(saveNameField, m_nameEdit->text());
                curr.saveDetail(&nm);
            }
        }

        QContactPhoneNumber phn = curr.detail(QContactPhoneNumber::DefinitionName);
        phn.setNumber(m_phoneEdit->text());
        curr.saveDetail(&phn);

        if (!m_emailEdit->isReadOnly()) {
            QContactEmailAddress em = curr.detail(QContactEmailAddress::DefinitionName);
            em.setEmailAddress(m_emailEdit->text());
            curr.saveDetail(&em);
        }

        if (!m_addrEdit->isReadOnly()) {
            QContactAddress adr = curr.detail(QContactAddress::DefinitionName);
            adr.setStreet(m_addrEdit->text());
            curr.saveDetail(&adr);
        }

        if (m_avatarBtn->isEnabled()) {
            QContactAvatar av = curr.detail(QContactAvatar::DefinitionName);
            av.setImageUrl(QUrl(m_newAvatarPath));
            curr.saveDetail(&av);

            QContactThumbnail thumb = curr.detail(QContactThumbnail::DefinitionName);
            QImage img(m_thumbnail);
            thumb.setThumbnail(img);
            curr.saveDetail(&thumb);
        }

        curr = m_manager->compatibleContact(curr);
        bool success = m_manager->saveContact(&curr);
        if (!success)
            QMessageBox::information(this, "Failed!", QString("Failed to save contact!\n(error code %1)").arg(m_manager->error()));
    }

    emit showListPage();
}

void ContactEditor::cancelClicked()
{
    emit showListPage();
}
<commit_msg>Disable thumbnails on maemo5 (revert previous commit)<commit_after>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
**     the names of its contributors may be used to endorse or promote
**     products derived from this software without specific prior written
**     permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
** $QT_END_LICENSE$
**
****************************************************************************/

#include "contacteditor.h"

#include <QtGui>

const int MAX_AVATAR_DISPLAY_SIZE = 120;

ContactEditor::ContactEditor(QWidget *parent)
        :QWidget(parent)
{
    m_manager = 0;
    m_contactId = QContactLocalId(0);

    m_nameEdit = new QLineEdit(this);
    m_phoneEdit = new QLineEdit(this);
    m_emailEdit = new QLineEdit(this);
    m_addrEdit = new QLineEdit(this);
    m_avatarBtn = new QPushButton(tr("Set picture"), this);
    m_clearAvatarBtn = new QPushButton(tr("Clear"), this);
    m_avatarView = new QLabel(this);
    connect(m_avatarBtn, SIGNAL(clicked()), this, SLOT(avatarClicked()));
    connect(m_clearAvatarBtn, SIGNAL(clicked()), this, SLOT(clearAvatarClicked()));

    QFormLayout *detailsLayout = new QFormLayout;
    QLabel *nameLabel = new QLabel(tr("Name"), this);
    QLabel *phoneLabel = new QLabel(tr("Phone"), this);
    QLabel *emailLabel = new QLabel(tr("Email"), this);
    QLabel *addressLabel = new QLabel(tr("Address"), this);
    QLabel *avatarLabel = new QLabel(tr("Picture"), this);
    QHBoxLayout *avatarBtnLayout = new QHBoxLayout;
    avatarBtnLayout->addWidget(m_avatarBtn);
    avatarBtnLayout->addWidget(m_clearAvatarBtn);
    if (QApplication::desktop()->availableGeometry().width() < 360) {
        // Narrow screen: put label on separate line to textbox
        detailsLayout->addRow(nameLabel);
        detailsLayout->addRow(m_nameEdit);
        detailsLayout->addRow(phoneLabel);
        detailsLayout->addRow(m_phoneEdit);
        detailsLayout->addRow(emailLabel);
        detailsLayout->addRow(m_emailEdit);
        detailsLayout->addRow(addressLabel);
        detailsLayout->addRow(m_addrEdit);
        detailsLayout->addRow(avatarLabel);
        detailsLayout->addRow(avatarBtnLayout);
        detailsLayout->addRow(m_avatarView);
    } else {
        // Wide screen: put label on same line as textbox
        detailsLayout->addRow(nameLabel, m_nameEdit);
        detailsLayout->addRow(phoneLabel, m_phoneEdit);
        detailsLayout->addRow(emailLabel, m_emailEdit);
        detailsLayout->addRow(addressLabel, m_addrEdit);
        detailsLayout->addRow(avatarLabel, avatarBtnLayout);
        detailsLayout->addRow("", m_avatarView);
    }
    detailsLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
    detailsLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);

    QScrollArea *detailsScrollArea = new QScrollArea(this);
    detailsScrollArea->setWidgetResizable(true);
    QWidget *detailsContainer = new QWidget(detailsScrollArea);
    detailsContainer->setLayout(detailsLayout);
    detailsScrollArea->setWidget(detailsContainer);

    QVBoxLayout *editLayout = new QVBoxLayout;
    editLayout->addWidget(detailsScrollArea);

#ifdef Q_OS_SYMBIAN
    // In symbian "save" and "cancel" buttons are softkeys.
    m_saveBtn = new QAction(tr("Save"), this);
    m_saveBtn->setSoftKeyRole(QAction::PositiveSoftKey);
    addAction(m_saveBtn);
    connect(m_saveBtn, SIGNAL(triggered(bool)), this, SLOT(saveClicked()));
    m_cancelBtn = new QAction(tr("Cancel"), this);
    m_cancelBtn->setSoftKeyRole(QAction::NegativeSoftKey);
    addAction(m_cancelBtn);
    connect(m_cancelBtn, SIGNAL(triggered(bool)), this, SLOT(cancelClicked()));
#else
    m_saveBtn = new QPushButton(tr("&Save"), this);
    m_saveBtn->setDefault(true);
    connect(m_saveBtn, SIGNAL(clicked()), this, SLOT(saveClicked()));
    m_cancelBtn = new QPushButton(tr("&Cancel"), this);
    connect(m_cancelBtn, SIGNAL(clicked()), this, SLOT(cancelClicked()));
    QHBoxLayout *btnLayout = new QHBoxLayout;
    btnLayout->addWidget(m_saveBtn);
    btnLayout->addWidget(m_cancelBtn);
    editLayout->addLayout(btnLayout);
#endif

    setLayout(editLayout);
}

ContactEditor::~ContactEditor()
{
}

void ContactEditor::setCurrentContact(QContactManager* manager, QContactLocalId currentId)
{
    m_manager = manager;
    m_contactId = currentId;
    m_newAvatarPath = QString();

    // Clear UI
    m_nameEdit->clear();
    m_phoneEdit->clear();
    m_emailEdit->clear();
    m_addrEdit->clear();

    if (manager == 0) {
        m_saveBtn->setEnabled(false);
        return;
    }

    // enable the UI.
    m_saveBtn->setEnabled(true);

    // otherwise, build from the contact details.
    QContact curr;
    if (m_contactId != QContactLocalId(0))
        curr = manager->contact(m_contactId);

    // Disable fields & buttons according to what the backend supports
    QMap<QString, QContactDetailDefinition> defs = m_manager->detailDefinitions(QContactType::TypeContact);

    // name
    //QContactName nm = curr.detail(QContactName::DefinitionName);
    if (m_contactId != QContactLocalId(0))
        m_nameEdit->setText(manager->synthesizedContactDisplayLabel(curr));

    // phonenumber
    QContactPhoneNumber phn = curr.detail(QContactPhoneNumber::DefinitionName);
    m_phoneEdit->setText(phn.value(QContactPhoneNumber::FieldNumber));

    // email
    if (defs.contains(QContactEmailAddress::DefinitionName)) {
        QContactEmailAddress em = curr.detail(QContactEmailAddress::DefinitionName);
        m_emailEdit->setText(em.value(QContactEmailAddress::FieldEmailAddress));
        m_emailEdit->setReadOnly(false);
    } else {
        m_emailEdit->setText("<not supported>");
        m_emailEdit->setReadOnly(true);
    }

    // address
    if (defs.contains(QContactAddress::DefinitionName)) {
        QContactAddress adr = curr.detail(QContactAddress::DefinitionName);
        m_addrEdit->setText(adr.value(QContactAddress::FieldStreet)); // ugly hack.
        m_addrEdit->setReadOnly(false);
    } else {
        m_addrEdit->setText("<not supported>");
        m_addrEdit->setReadOnly(true);
    }

    // avatar viewer
    if (defs.contains(QContactAvatar::DefinitionName)
        && defs.contains(QContactThumbnail::DefinitionName)) {
        m_avatarBtn->setEnabled(true);
        QContactAvatar av = curr.detail(QContactAvatar::DefinitionName);
        QContactThumbnail thumb = curr.detail(QContactThumbnail::DefinitionName);
        m_avatarView->clear();
        m_newAvatarPath = av.imageUrl().toLocalFile();
        m_thumbnail = thumb.thumbnail();
        if (m_thumbnail.isNull()) {
            if (m_newAvatarPath.isEmpty()) {
                m_avatarView->clear();
                m_clearAvatarBtn->setDisabled(true);
            } else {
                setAvatarPixmap(QPixmap(av.imageUrl().toLocalFile()));
                m_thumbnail = QImage(av.imageUrl().toLocalFile());
            }
        } else {
            setAvatarPixmap(QPixmap::fromImage(m_thumbnail));
        }
    } else {
        m_avatarBtn->setDisabled(true);
        m_clearAvatarBtn->setDisabled(true);
    }
}

QString ContactEditor::nameField()
{
    // return the field which the name data should be saved in.
    if (!m_manager)
        return QString();

    QMap<QString, QContactDetailDefinition> defs = m_manager->detailDefinitions(QContactType::TypeContact);
    QContactDetailDefinition nameDef = defs.value(QContactName::DefinitionName);
    if (nameDef.fields().keys().contains(QContactName::FieldCustomLabel)) {
        return QString(QLatin1String(QContactName::FieldCustomLabel));
    } else if (nameDef.fields().keys().contains(QContactName::FieldFirstName)) {
        return QString(QLatin1String(QContactName::FieldFirstName));
    } else {
        return QString();
    }
}

void ContactEditor::setAvatarPixmap(const QPixmap &pixmap)
{
    if (pixmap.isNull())
        return;
    QPixmap scaled = pixmap.scaled(QSize(MAX_AVATAR_DISPLAY_SIZE, MAX_AVATAR_DISPLAY_SIZE),
                                   Qt::KeepAspectRatio,
                                   Qt::SmoothTransformation);
    m_avatarView->setPixmap(scaled);
    m_avatarView->setMaximumSize(scaled.size());
    m_clearAvatarBtn->setEnabled(true);
}

void ContactEditor::clearAvatarClicked()
{
    m_avatarView->clear();
    m_thumbnail = QImage();
    m_newAvatarPath.clear();
    m_clearAvatarBtn->setDisabled(true);
}

void ContactEditor::avatarClicked()
{
    // put up a file dialog, and update the new avatar path.
    QString fileName = QFileDialog::getOpenFileName(this,
       tr("Select Contact Picture"), ".", tr("Image Files (*.png *.jpg *.bmp)"));

    if (!fileName.isEmpty()) {
        m_newAvatarPath = fileName;
        m_thumbnail = QImage(m_newAvatarPath);
        setAvatarPixmap(QPixmap::fromImage(m_thumbnail));
    }
}

void ContactEditor::saveClicked()
{
    if (!m_manager) {
        qWarning() << "No manager selected; cannot save.";
    } else {
        QContact curr;
        if (m_contactId != QContactLocalId(0))
            curr = m_manager->contact(m_contactId);

        if (m_nameEdit->text().isEmpty()) {
            QMessageBox::information(this, "Failed!", "You must give a name for the contact!");
            return;
        }

        if (m_nameEdit->text() != m_manager->synthesizedContactDisplayLabel(curr)) {
            // if the name has changed (ie, is different to the synthed label) then save it as a custom label.
            QString saveNameField = nameField();
            if (!saveNameField.isEmpty()) {
                QContactName nm = curr.detail(QContactName::DefinitionName);
                nm.setValue(saveNameField, m_nameEdit->text());
                curr.saveDetail(&nm);
            }
        }

        QContactPhoneNumber phn = curr.detail(QContactPhoneNumber::DefinitionName);
        phn.setNumber(m_phoneEdit->text());
        curr.saveDetail(&phn);

        if (!m_emailEdit->isReadOnly()) {
            QContactEmailAddress em = curr.detail(QContactEmailAddress::DefinitionName);
            em.setEmailAddress(m_emailEdit->text());
            curr.saveDetail(&em);
        }

        if (!m_addrEdit->isReadOnly()) {
            QContactAddress adr = curr.detail(QContactAddress::DefinitionName);
            adr.setStreet(m_addrEdit->text());
            curr.saveDetail(&adr);
        }

        if (m_avatarBtn->isEnabled()) {
            QContactAvatar av = curr.detail(QContactAvatar::DefinitionName);
            av.setImageUrl(QUrl(m_newAvatarPath));
            curr.saveDetail(&av);

            QContactThumbnail thumb = curr.detail(QContactThumbnail::DefinitionName);
            QImage img(m_thumbnail);
            thumb.setThumbnail(img);
            curr.saveDetail(&thumb);
        }

        bool success = m_manager->saveContact(&curr);
        if (!success)
            QMessageBox::information(this, "Failed!", QString("Failed to save contact!\n(error code %1)").arg(m_manager->error()));
    }

    emit showListPage();
}

void ContactEditor::cancelClicked()
{
    emit showListPage();
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2021-2021 ArangoDB GmbH, Cologne, Germany
///
/// Licensed 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Lars Maier
////////////////////////////////////////////////////////////////////////////////

#pragma once
#include "Replication2/Streams/StreamInformationBlock.h"
#include "Replication2/Streams/Streams.h"

namespace arangodb::replication2::streams {

template <StreamId Id, typename Type, typename Tags>
auto StreamInformationBlock<stream_descriptor<Id, Type, Tags>>::getTransientContainer()
    -> TransientType& {
  if (!std::holds_alternative<TransientType>(_container)) {
    _container = std::get<ContainerType>(_container).transient();
  }
  return std::get<TransientType>(_container);
}

template <StreamId Id, typename Type, typename Tags>
auto StreamInformationBlock<stream_descriptor<Id, Type, Tags>>::getPersistentContainer()
    -> ContainerType& {
  if (!std::holds_alternative<ContainerType>(_container)) {
    _container = std::get<TransientType>(_container).persistent();
  }
  return std::get<ContainerType>(_container);
}

template <StreamId Id, typename Type, typename Tags>
auto StreamInformationBlock<stream_descriptor<Id, Type, Tags>>::appendEntry(LogIndex index,
                                                                            Type t) {
  getTransientContainer().push_back(EntryType{index, std::move(t)});
}

template <StreamId Id, typename Type, typename Tags>
auto StreamInformationBlock<stream_descriptor<Id, Type, Tags>>::getWaitForResolveSet(LogIndex commitIndex)
    -> std::multimap<LogIndex, futures::Promise<WaitForResult>> {
  WaitForQueue toBeResolved;
  auto const end = _waitForQueue.upper_bound(commitIndex);
  for (auto it = _waitForQueue.begin(); it != end;) {
    toBeResolved.insert(_waitForQueue.extract(it++));
  }
  return toBeResolved;
}

template <StreamId Id, typename Type, typename Tags>
auto StreamInformationBlock<stream_descriptor<Id, Type, Tags>>::registerWaitFor(LogIndex index)
    -> futures::Future<WaitForResult> {
  return _waitForQueue.emplace(index, futures::Promise<WaitForResult>{})->second.getFuture();
}

template <StreamId Id, typename Type, typename Tags>
auto StreamInformationBlock<stream_descriptor<Id, Type, Tags>>::getIterator()
    -> std::unique_ptr<Iterator> {
  auto log = getPersistentContainer();

  struct Iterator : TypedLogRangeIterator<StreamEntryView<Type>> {
    ContainerType log;
    typename ContainerType::iterator current;

    auto next() -> std::optional<StreamEntryView<Type>> override {
      if (current != std::end(log)) {
        auto view = std::make_pair(current->first, std::cref(current->second));
        ++current;
        return view;
      }
      return std::nullopt;
    }

    [[nodiscard]] auto range() const noexcept -> LogRange override {
      abort(); // TODO
    }

    explicit Iterator(ContainerType log)
        : log(std::move(log)), current(this->log.begin()) {}
  };

  return std::make_unique<Iterator>(std::move(log));
}

template <StreamId Id, typename Type, typename Tags>
auto StreamInformationBlock<stream_descriptor<Id, Type, Tags>>::getIteratorRange(LogIndex start, LogIndex stop)
    -> std::unique_ptr<Iterator> {
  TRI_ASSERT(stop >= start);

  auto const log = getPersistentContainer();

  using ContainerIterator = typename ContainerType::iterator;

  struct Iterator : TypedLogRangeIterator<StreamEntryView<Type>> {
    ContainerType _log;
    ContainerIterator current;
    LogIndex start, stop;

    auto next() -> std::optional<StreamEntryView<Type>> override {
      if (current != std::end(_log) && current->first < stop) {
        auto view = std::make_pair(current->first, std::cref(current->second));
        ++current;
        return view;
      }
      return std::nullopt;
    }
    [[nodiscard]] auto range() const noexcept -> LogRange override {
      return {start, stop};
    }

    explicit Iterator(ContainerType log, LogIndex start, LogIndex stop)
        : _log(std::move(log)),
          current(std::lower_bound(std::begin(_log), std::end(_log), start,
                                   [](StreamEntry<Type> const& left, LogIndex index) {
                                     return left.first < index;
                                   })),
          // cppcheck-suppress 	selfInitialization
          start(start),
          // cppcheck-suppress 	selfInitialization
          stop(stop) {}
  };
  return std::make_unique<Iterator>(std::move(log), start, stop);
}

}  // namespace arangodb::replication2::streams
<commit_msg>Fixing CppCheck warnings. (#14854)<commit_after>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2021-2021 ArangoDB GmbH, Cologne, Germany
///
/// Licensed 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Lars Maier
////////////////////////////////////////////////////////////////////////////////

#pragma once
#include "Replication2/Streams/StreamInformationBlock.h"
#include "Replication2/Streams/Streams.h"

namespace arangodb::replication2::streams {

template <StreamId Id, typename Type, typename Tags>
auto StreamInformationBlock<stream_descriptor<Id, Type, Tags>>::getTransientContainer()
    -> TransientType& {
  if (!std::holds_alternative<TransientType>(_container)) {
    _container = std::get<ContainerType>(_container).transient();
  }
  return std::get<TransientType>(_container);
}

template <StreamId Id, typename Type, typename Tags>
auto StreamInformationBlock<stream_descriptor<Id, Type, Tags>>::getPersistentContainer()
    -> ContainerType& {
  if (!std::holds_alternative<ContainerType>(_container)) {
    _container = std::get<TransientType>(_container).persistent();
  }
  return std::get<ContainerType>(_container);
}

template <StreamId Id, typename Type, typename Tags>
auto StreamInformationBlock<stream_descriptor<Id, Type, Tags>>::appendEntry(LogIndex index,
                                                                            Type t) {
  getTransientContainer().push_back(EntryType{index, std::move(t)});
}

template <StreamId Id, typename Type, typename Tags>
auto StreamInformationBlock<stream_descriptor<Id, Type, Tags>>::getWaitForResolveSet(LogIndex commitIndex)
    -> std::multimap<LogIndex, futures::Promise<WaitForResult>> {
  WaitForQueue toBeResolved;
  auto const end = _waitForQueue.upper_bound(commitIndex);
  for (auto it = _waitForQueue.begin(); it != end;) {
    toBeResolved.insert(_waitForQueue.extract(it++));
  }
  return toBeResolved;
}

template <StreamId Id, typename Type, typename Tags>
auto StreamInformationBlock<stream_descriptor<Id, Type, Tags>>::registerWaitFor(LogIndex index)
    -> futures::Future<WaitForResult> {
  return _waitForQueue.emplace(index, futures::Promise<WaitForResult>{})->second.getFuture();
}

template <StreamId Id, typename Type, typename Tags>
auto StreamInformationBlock<stream_descriptor<Id, Type, Tags>>::getIterator()
    -> std::unique_ptr<Iterator> {
  auto log = getPersistentContainer();

  struct Iterator : TypedLogRangeIterator<StreamEntryView<Type>> {
    ContainerType log;
    typename ContainerType::iterator current;

    auto next() -> std::optional<StreamEntryView<Type>> override {
      if (current != std::end(log)) {
        auto view = std::make_pair(current->first, std::cref(current->second));
        ++current;
        return view;
      }
      return std::nullopt;
    }

    [[nodiscard]] auto range() const noexcept -> LogRange override {
      abort(); // TODO
    }

    explicit Iterator(ContainerType log)
        : log(std::move(log)), current(this->log.begin()) {}
  };

  return std::make_unique<Iterator>(std::move(log));
}

template <StreamId Id, typename Type, typename Tags>
auto StreamInformationBlock<stream_descriptor<Id, Type, Tags>>::getIteratorRange(LogIndex start, LogIndex stop)
    -> std::unique_ptr<Iterator> {
  TRI_ASSERT(stop >= start);

  auto const log = getPersistentContainer();

  using ContainerIterator = typename ContainerType::iterator;

  struct Iterator : TypedLogRangeIterator<StreamEntryView<Type>> {
    ContainerType _log;
    ContainerIterator _current;
    LogIndex _start, _stop;

    auto next() -> std::optional<StreamEntryView<Type>> override {
      if (_current != std::end(_log) && _current->first < _stop) {
        auto view = std::make_pair(_current->first, std::cref(_current->second));
        ++_current;
        return view;
      }
      return std::nullopt;
    }
    [[nodiscard]] auto range() const noexcept -> LogRange override {
      return {_start, _stop};
    }

    explicit Iterator(ContainerType log, LogIndex start, LogIndex stop)
        : _log(std::move(log)),
          _current(std::lower_bound(std::begin(_log), std::end(_log), start,
                                   [](StreamEntry<Type> const& left, LogIndex index) {
                                     return left.first < index;
                                   })),
          // cppcheck-suppress 	selfInitialization
          _start(start),
          // cppcheck-suppress 	selfInitialization
          _stop(stop) {}
  };
  return std::make_unique<Iterator>(std::move(log), start, stop);
}

}  // namespace arangodb::replication2::streams
<|endoftext|>


<commit_before>/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkCanvas.h"
#include "SkCommandLineFlags.h"
#include "SkDevice.h"
#include "SkGraphics.h"
#include "SkImageDecoder.h"
#include "SkImageEncoder.h"
#include "SkOSFile.h"
#include "SkPdfRenderer.h"
#include "SkPicture.h"
#include "SkStream.h"
#include "SkTypeface.h"
#include "SkTArray.h"
#include "SkNulCanvas.h"

#if SK_SUPPORT_GPU
#include "GrContextFactory.h"
#include "GrContext.h"
#include "SkGpuDevice.h"
#endif

DEFINE_string2(readPath, r, "", "pdf files or directories of pdf files to process.");
DEFINE_string2(writePath, w, "", "Directory to write the rendered pages.");
DEFINE_bool2(noExtensionForOnePagePdf, n, false, "No page extension if only one page.");
DEFINE_bool2(showMemoryUsage, m, false, "Show Memory usage.");
DEFINE_string2(pages, p, "all", "What pages to render and how:\n"
                                "\tall - all pages\n"
                                "\treverse - all pages, in reverse order\n"
                                "\tfirst - first page\n"
                                "\tlast - last page\n"
                                "\tnumber - a specific page number\n"
               );
DEFINE_double(DPI, 72, "DPI to be used for rendering (scale).");
DEFINE_int32(benchLoad, 0, "Load the pdf file minimally N times, without any rendering and \n"
             "\tminimal parsing to ensure correctness. Default 0 (disabled).");
DEFINE_int32(benchRender, 0, "Render the pdf content N times. Default 0 (disabled)");
DEFINE_string2(config, c, "8888", "Canvas to render:\n"
                                  "\t8888 - argb\n"
#if SK_SUPPORT_GPU
                                  "\tgpu: use the gpu\n"
#endif
                                  "\tnul - render in null canvas, any draw will just return.\n"
               );
DEFINE_bool2(transparentBackground, t, false, "Make background transparent instead of white.");

/**
 * Given list of directories and files to use as input, expects to find .pdf
 * files and it will convert them to .png files writing them in the same directory
 * one file for each page.
 *
 * Returns zero exit code if all .pdf files were converted successfully,
 * otherwise returns error code 1.
 */

static const char PDF_FILE_EXTENSION[] = "pdf";
static const char PNG_FILE_EXTENSION[] = "png";

/** Replaces the extension of a file.
 * @param path File name whose extension will be changed.
 * @param old_extension The old extension.
 * @param new_extension The new extension.
 * @returns false if the file did not has the expected extension.
 *  if false is returned, contents of path are undefined.
 */
static bool add_page_and_replace_filename_extension(SkString* path, int page,
                                       const char old_extension[],
                                       const char new_extension[]) {
    if (path->endsWith(old_extension)) {
        path->remove(path->size() - strlen(old_extension),
                     strlen(old_extension));
        if (!path->endsWith(".")) {
            return false;
        }
        if (page >= 0) {
            path->appendf("%i.", page);
        }
        path->append(new_extension);
        return true;
    }
    return false;
}

static void make_filepath(SkString* path, const SkString& dir, const SkString& name) {
    size_t len = dir.size();
    path->set(dir);
    if (0 < len  && '/' != dir[len - 1]) {
        path->append("/");
    }
    path->append(name);
}

static bool is_path_seperator(const char chr) {
#if defined(SK_BUILD_FOR_WIN)
    return chr == '\\' || chr == '/';
#else
    return chr == '/';
#endif
}

static void get_basename(SkString* basename, const SkString& path) {
    if (path.size() == 0) {
        basename->reset();
        return;
    }

    size_t end = path.size() - 1;

    // Paths pointing to directories often have a trailing slash,
    // we remove it so the name is not empty
    if (is_path_seperator(path[end])) {
        if (end == 0) {
            basename->reset();
            return;
        }

        end -= 1;
    }

    size_t i = end;
    do {
        --i;
        if (is_path_seperator(path[i])) {
              const char* basenameStart = path.c_str() + i + 1;
              size_t basenameLength = end - i;
              basename->set(basenameStart, basenameLength);
              return;
        }
    } while (i > 0);

    basename->set(path.c_str(), end + 1);
}

/** Builds the output filename. path = dir/name, and it replaces expected
 * .skp extension with .pdf extention.
 * @param path Output filename.
 * @param name The name of the file.
 * @returns false if the file did not has the expected extension.
 *  if false is returned, contents of path are undefined.
 */
static bool make_output_filepath(SkString* path, const SkString& dir,
                                 const SkString& name,
                                 int page) {
    make_filepath(path, dir, name);
    return add_page_and_replace_filename_extension(path, page,
                                                   PDF_FILE_EXTENSION,
                                                   PNG_FILE_EXTENSION);
}

static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color) {
    bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);

    bitmap->allocPixels();
    bitmap->eraseColor(color);
}

/** Write the output of pdf renderer to a file.
 * @param outputDir Output dir.
 * @param inputFilename The skp file that was read.
 * @param renderer The object responsible to write the pdf file.
 * @param page -1 means there is only one page (0), and render in a file without page extension
 */

extern "C" SkBitmap* gDumpBitmap;
extern "C" SkCanvas* gDumpCanvas;

#if SK_SUPPORT_GPU
GrContextFactory gContextFactory;
#endif

static bool render_page(const SkString& outputDir,
                        const SkString& inputFilename,
                        const SkPdfRenderer& renderer,
                        int page) {
    SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);

    // Exercise all pdf codepaths as in normal rendering, but no actual bits are changed.
    if (!FLAGS_config.isEmpty() && strcmp(FLAGS_config[0], "nul") == 0) {
        SkBitmap bitmap;
        SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
        SkNulCanvas canvas(device);
        renderer.renderPage(page < 0 ? 0 : page, &canvas, rect);
    } else {
        // 8888
        SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);

        SkBitmap bitmap;
        SkScalar width = SkScalarMul(rect.width(),  SkDoubleToScalar(sqrt(FLAGS_DPI / 72.0)));
        SkScalar height = SkScalarMul(rect.height(),  SkDoubleToScalar(sqrt(FLAGS_DPI / 72.0)));

        rect = SkRect::MakeWH(width, height);

        SkColor background = FLAGS_transparentBackground ? SK_ColorTRANSPARENT : SK_ColorWHITE;

#ifdef PDF_DEBUG_3X
        setup_bitmap(&bitmap, 3 * (int)SkScalarToDouble(width), 3 * (int)SkScalarToDouble(height),
                     background);
#else
        setup_bitmap(&bitmap, (int)SkScalarToDouble(width), (int)SkScalarToDouble(height),
                     background);
#endif
        SkAutoTUnref<SkBaseDevice> device;
        if (strcmp(FLAGS_config[0], "8888") == 0) {
            device.reset(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
        }
#if SK_SUPPORT_GPU
        else if (strcmp(FLAGS_config[0], "gpu") == 0) {
            SkAutoTUnref<GrSurface> target;
            GrContext* gr = gContextFactory.get(GrContextFactory::kNative_GLContextType);
            if (gr) {
                // create a render target to back the device
                GrTextureDesc desc;
                desc.fConfig = kSkia8888_GrPixelConfig;
                desc.fFlags = kRenderTarget_GrTextureFlagBit;
                desc.fWidth = width;
                desc.fHeight = height;
                desc.fSampleCnt = 0;
                target.reset(gr->createUncachedTexture(desc, NULL, 0));
            }
            if (NULL == target.get()) {
                SkASSERT(0);
                return false;
            }

            device.reset(SkGpuDevice::Create(target));
        }
#endif
        else {
            SkDebugf("unknown --config: %s\n", FLAGS_config[0]);
            return false;
        }
        SkCanvas canvas(device);

        gDumpBitmap = &bitmap;

        gDumpCanvas = &canvas;
        renderer.renderPage(page < 0 ? 0 : page, &canvas, rect);

        SkString outputPath;
        if (!make_output_filepath(&outputPath, outputDir, inputFilename, page)) {
            return false;
        }
        SkImageEncoder::EncodeFile(outputPath.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);

        if (FLAGS_showMemoryUsage) {
            SkDebugf("Memory usage after page %i rendered: %u\n",
                     page < 0 ? 0 : page, (unsigned int)renderer.bytesUsed());
        }
    }
    return true;
}

/** Reads an skp file, renders it to pdf and writes the output to a pdf file
 * @param inputPath The skp file to be read.
 * @param outputDir Output dir.
 * @param renderer The object responsible to render the skp object into pdf.
 */
static bool process_pdf(const SkString& inputPath, const SkString& outputDir,
                        SkPdfRenderer& renderer) {
    SkDebugf("Loading PDF:  %s\n", inputPath.c_str());

    SkString inputFilename;
    get_basename(&inputFilename, inputPath);

    bool success = true;

    success = renderer.load(inputPath);
    if (FLAGS_showMemoryUsage) {
        SkDebugf("Memory usage after load: %u\n", (unsigned int)renderer.bytesUsed());
    }

    // TODO(edisonn): bench timers
    if (FLAGS_benchLoad > 0) {
        for (int i = 0 ; i < FLAGS_benchLoad; i++) {
            success = renderer.load(inputPath) && success;
            if (FLAGS_showMemoryUsage) {
                SkDebugf("Memory usage after load %i number : %u\n", i,
                         (unsigned int)renderer.bytesUsed());
            }
        }
    }

    if (success) {
        if (!renderer.pages())
        {
            SkDebugf("ERROR: Empty PDF Document %s\n", inputPath.c_str());
            return false;
        } else {
            for (int i = 0; i < FLAGS_benchRender + 1; i++) {
                // TODO(edisonn) if (i == 1) start timer
                if (strcmp(FLAGS_pages[0], "all") == 0) {
                    for (int pn = 0; pn < renderer.pages(); ++pn) {
                        success = render_page(
                                outputDir,
                                inputFilename,
                                renderer,
                                FLAGS_noExtensionForOnePagePdf && renderer.pages() == 1 ? -1 :
                                                                                pn) &&
                                     success;
                    }
                } else if (strcmp(FLAGS_pages[0], "reverse") == 0) {
                    for (int pn = renderer.pages() - 1; pn >= 0; --pn) {
                        success = render_page(
                                outputDir,
                                inputFilename,
                                renderer,
                                FLAGS_noExtensionForOnePagePdf && renderer.pages() == 1 ? -1 :
                                                                                pn) &&
                                   success;
                    }
                } else if (strcmp(FLAGS_pages[0], "first") == 0) {
                    success = render_page(
                            outputDir,
                            inputFilename,
                            renderer,
                            FLAGS_noExtensionForOnePagePdf && renderer.pages() == 1 ? -1 : 0) &&
                                success;
                } else if (strcmp(FLAGS_pages[0], "last") == 0) {
                    success = render_page(
                            outputDir,
                            inputFilename,
                            renderer,
                            FLAGS_noExtensionForOnePagePdf &&
                                    renderer.pages() == 1 ? -1 : renderer.pages() - 1) && success;
                } else {
                    int pn = atoi(FLAGS_pages[0]);
                    success = render_page(outputDir, inputFilename, renderer,
                                          FLAGS_noExtensionForOnePagePdf &&
                                              renderer.pages() == 1 ? -1 : pn) && success;
                }
            }
        }
    }

    if (!success) {
        SkDebugf("Failures for file %s\n", inputPath.c_str());
    }

    return success;
}

/** For each file in the directory or for the file passed in input, call
 * parse_pdf.
 * @param input A directory or an pdf file.
 * @param outputDir Output dir.
 * @param renderer The object responsible to render the skp object into pdf.
 */
static int process_input(const char* input, const SkString& outputDir,
                         SkPdfRenderer& renderer) {
    int failures = 0;
    if (sk_isdir(input)) {
        SkOSFile::Iter iter(input, PDF_FILE_EXTENSION);
        SkString inputFilename;
        while (iter.next(&inputFilename)) {
            SkString inputPath;
            SkString _input;
            _input.append(input);
            make_filepath(&inputPath, _input, inputFilename);
            if (!process_pdf(inputPath, outputDir, renderer)) {
                ++failures;
            }
        }
    } else {
        SkString inputPath(input);
        if (!process_pdf(inputPath, outputDir, renderer)) {
            ++failures;
        }
    }
    return failures;
}

int tool_main(int argc, char** argv);
int tool_main(int argc, char** argv) {
    SkCommandLineFlags::SetUsage("Parse and Render .pdf files (pdf viewer).");
    SkCommandLineFlags::Parse(argc, argv);

    if (FLAGS_readPath.isEmpty()) {
        SkDebugf(".pdf files or directories are required.\n");
        exit(-1);
    }

    SkPdfRenderer renderer;

    SkString outputDir;
    if (FLAGS_writePath.count() == 1) {
        outputDir.set(FLAGS_writePath[0]);
    }

    int failures = 0;
    for (int i = 0; i < FLAGS_readPath.count(); i ++) {
        failures += process_input(FLAGS_readPath[i], outputDir, renderer);
        renderer.unload();
    }

    reportPdfRenderStats();

    if (failures != 0) {
        SkDebugf("Failed to render %i PDFs.\n", failures);
        return 1;
    }

    return 0;
}

#if !defined SK_BUILD_FOR_IOS
int main(int argc, char * const argv[]) {
    return tool_main(argc, (char**) argv);
}
#endif
<commit_msg>Use SkPathJoin and SkBasename in Pdfviewer.<commit_after>/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkCanvas.h"
#include "SkCommandLineFlags.h"
#include "SkDevice.h"
#include "SkGraphics.h"
#include "SkImageDecoder.h"
#include "SkImageEncoder.h"
#include "SkOSFile.h"
#include "SkPdfRenderer.h"
#include "SkPicture.h"
#include "SkStream.h"
#include "SkTypeface.h"
#include "SkTArray.h"
#include "SkNulCanvas.h"

#if SK_SUPPORT_GPU
#include "GrContextFactory.h"
#include "GrContext.h"
#include "SkGpuDevice.h"
#endif

DEFINE_string2(readPath, r, "", "pdf files or directories of pdf files to process.");
DEFINE_string2(writePath, w, "", "Directory to write the rendered pages.");
DEFINE_bool2(noExtensionForOnePagePdf, n, false, "No page extension if only one page.");
DEFINE_bool2(showMemoryUsage, m, false, "Show Memory usage.");
DEFINE_string2(pages, p, "all", "What pages to render and how:\n"
                                "\tall - all pages\n"
                                "\treverse - all pages, in reverse order\n"
                                "\tfirst - first page\n"
                                "\tlast - last page\n"
                                "\tnumber - a specific page number\n"
               );
DEFINE_double(DPI, 72, "DPI to be used for rendering (scale).");
DEFINE_int32(benchLoad, 0, "Load the pdf file minimally N times, without any rendering and \n"
             "\tminimal parsing to ensure correctness. Default 0 (disabled).");
DEFINE_int32(benchRender, 0, "Render the pdf content N times. Default 0 (disabled)");
DEFINE_string2(config, c, "8888", "Canvas to render:\n"
                                  "\t8888 - argb\n"
#if SK_SUPPORT_GPU
                                  "\tgpu: use the gpu\n"
#endif
                                  "\tnul - render in null canvas, any draw will just return.\n"
               );
DEFINE_bool2(transparentBackground, t, false, "Make background transparent instead of white.");

/**
 * Given list of directories and files to use as input, expects to find .pdf
 * files and it will convert them to .png files writing them in the same directory
 * one file for each page.
 *
 * Returns zero exit code if all .pdf files were converted successfully,
 * otherwise returns error code 1.
 */

static const char PDF_FILE_EXTENSION[] = "pdf";
static const char PNG_FILE_EXTENSION[] = "png";

/** Replaces the extension of a file.
 * @param path File name whose extension will be changed.
 * @param old_extension The old extension.
 * @param new_extension The new extension.
 * @returns false if the file did not has the expected extension.
 *  if false is returned, contents of path are undefined.
 */
static bool add_page_and_replace_filename_extension(SkString* path, int page,
                                       const char old_extension[],
                                       const char new_extension[]) {
    if (path->endsWith(old_extension)) {
        path->remove(path->size() - strlen(old_extension),
                     strlen(old_extension));
        if (!path->endsWith(".")) {
            return false;
        }
        if (page >= 0) {
            path->appendf("%i.", page);
        }
        path->append(new_extension);
        return true;
    }
    return false;
}

/** Builds the output filename. path = dir/name, and it replaces expected
 * .skp extension with .pdf extention.
 * @param path Output filename.
 * @param name The name of the file.
 * @returns false if the file did not has the expected extension.
 *  if false is returned, contents of path are undefined.
 */
static bool make_output_filepath(SkString* path, const SkString& dir,
                                 const SkString& name,
                                 int page) {
    *path = SkOSPath::SkPathJoin(dir.c_str(), name.c_str());
    return add_page_and_replace_filename_extension(path, page,
                                                   PDF_FILE_EXTENSION,
                                                   PNG_FILE_EXTENSION);
}

static void setup_bitmap(SkBitmap* bitmap, int width, int height, SkColor color) {
    bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);

    bitmap->allocPixels();
    bitmap->eraseColor(color);
}

/** Write the output of pdf renderer to a file.
 * @param outputDir Output dir.
 * @param inputFilename The skp file that was read.
 * @param renderer The object responsible to write the pdf file.
 * @param page -1 means there is only one page (0), and render in a file without page extension
 */

extern "C" SkBitmap* gDumpBitmap;
extern "C" SkCanvas* gDumpCanvas;

#if SK_SUPPORT_GPU
GrContextFactory gContextFactory;
#endif

static bool render_page(const SkString& outputDir,
                        const SkString& inputFilename,
                        const SkPdfRenderer& renderer,
                        int page) {
    SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);

    // Exercise all pdf codepaths as in normal rendering, but no actual bits are changed.
    if (!FLAGS_config.isEmpty() && strcmp(FLAGS_config[0], "nul") == 0) {
        SkBitmap bitmap;
        SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
        SkNulCanvas canvas(device);
        renderer.renderPage(page < 0 ? 0 : page, &canvas, rect);
    } else {
        // 8888
        SkRect rect = renderer.MediaBox(page < 0 ? 0 :page);

        SkBitmap bitmap;
        SkScalar width = SkScalarMul(rect.width(),  SkDoubleToScalar(sqrt(FLAGS_DPI / 72.0)));
        SkScalar height = SkScalarMul(rect.height(),  SkDoubleToScalar(sqrt(FLAGS_DPI / 72.0)));

        rect = SkRect::MakeWH(width, height);

        SkColor background = FLAGS_transparentBackground ? SK_ColorTRANSPARENT : SK_ColorWHITE;

#ifdef PDF_DEBUG_3X
        setup_bitmap(&bitmap, 3 * (int)SkScalarToDouble(width), 3 * (int)SkScalarToDouble(height),
                     background);
#else
        setup_bitmap(&bitmap, (int)SkScalarToDouble(width), (int)SkScalarToDouble(height),
                     background);
#endif
        SkAutoTUnref<SkBaseDevice> device;
        if (strcmp(FLAGS_config[0], "8888") == 0) {
            device.reset(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
        }
#if SK_SUPPORT_GPU
        else if (strcmp(FLAGS_config[0], "gpu") == 0) {
            SkAutoTUnref<GrSurface> target;
            GrContext* gr = gContextFactory.get(GrContextFactory::kNative_GLContextType);
            if (gr) {
                // create a render target to back the device
                GrTextureDesc desc;
                desc.fConfig = kSkia8888_GrPixelConfig;
                desc.fFlags = kRenderTarget_GrTextureFlagBit;
                desc.fWidth = width;
                desc.fHeight = height;
                desc.fSampleCnt = 0;
                target.reset(gr->createUncachedTexture(desc, NULL, 0));
            }
            if (NULL == target.get()) {
                SkASSERT(0);
                return false;
            }

            device.reset(SkGpuDevice::Create(target));
        }
#endif
        else {
            SkDebugf("unknown --config: %s\n", FLAGS_config[0]);
            return false;
        }
        SkCanvas canvas(device);

        gDumpBitmap = &bitmap;

        gDumpCanvas = &canvas;
        renderer.renderPage(page < 0 ? 0 : page, &canvas, rect);

        SkString outputPath;
        if (!make_output_filepath(&outputPath, outputDir, inputFilename, page)) {
            return false;
        }
        SkImageEncoder::EncodeFile(outputPath.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);

        if (FLAGS_showMemoryUsage) {
            SkDebugf("Memory usage after page %i rendered: %u\n",
                     page < 0 ? 0 : page, (unsigned int)renderer.bytesUsed());
        }
    }
    return true;
}

/** Reads an skp file, renders it to pdf and writes the output to a pdf file
 * @param inputPath The skp file to be read.
 * @param outputDir Output dir.
 * @param renderer The object responsible to render the skp object into pdf.
 */
static bool process_pdf(const SkString& inputPath, const SkString& outputDir,
                        SkPdfRenderer& renderer) {
    SkDebugf("Loading PDF:  %s\n", inputPath.c_str());

    SkString inputFilename = SkOSPath::SkBasename(inputPath.c_str());

    bool success = true;

    success = renderer.load(inputPath);
    if (FLAGS_showMemoryUsage) {
        SkDebugf("Memory usage after load: %u\n", (unsigned int)renderer.bytesUsed());
    }

    // TODO(edisonn): bench timers
    if (FLAGS_benchLoad > 0) {
        for (int i = 0 ; i < FLAGS_benchLoad; i++) {
            success = renderer.load(inputPath) && success;
            if (FLAGS_showMemoryUsage) {
                SkDebugf("Memory usage after load %i number : %u\n", i,
                         (unsigned int)renderer.bytesUsed());
            }
        }
    }

    if (success) {
        if (!renderer.pages())
        {
            SkDebugf("ERROR: Empty PDF Document %s\n", inputPath.c_str());
            return false;
        } else {
            for (int i = 0; i < FLAGS_benchRender + 1; i++) {
                // TODO(edisonn) if (i == 1) start timer
                if (strcmp(FLAGS_pages[0], "all") == 0) {
                    for (int pn = 0; pn < renderer.pages(); ++pn) {
                        success = render_page(
                                outputDir,
                                inputFilename,
                                renderer,
                                FLAGS_noExtensionForOnePagePdf && renderer.pages() == 1 ? -1 :
                                                                                pn) &&
                                     success;
                    }
                } else if (strcmp(FLAGS_pages[0], "reverse") == 0) {
                    for (int pn = renderer.pages() - 1; pn >= 0; --pn) {
                        success = render_page(
                                outputDir,
                                inputFilename,
                                renderer,
                                FLAGS_noExtensionForOnePagePdf && renderer.pages() == 1 ? -1 :
                                                                                pn) &&
                                   success;
                    }
                } else if (strcmp(FLAGS_pages[0], "first") == 0) {
                    success = render_page(
                            outputDir,
                            inputFilename,
                            renderer,
                            FLAGS_noExtensionForOnePagePdf && renderer.pages() == 1 ? -1 : 0) &&
                                success;
                } else if (strcmp(FLAGS_pages[0], "last") == 0) {
                    success = render_page(
                            outputDir,
                            inputFilename,
                            renderer,
                            FLAGS_noExtensionForOnePagePdf &&
                                    renderer.pages() == 1 ? -1 : renderer.pages() - 1) && success;
                } else {
                    int pn = atoi(FLAGS_pages[0]);
                    success = render_page(outputDir, inputFilename, renderer,
                                          FLAGS_noExtensionForOnePagePdf &&
                                              renderer.pages() == 1 ? -1 : pn) && success;
                }
            }
        }
    }

    if (!success) {
        SkDebugf("Failures for file %s\n", inputPath.c_str());
    }

    return success;
}

/** For each file in the directory or for the file passed in input, call
 * parse_pdf.
 * @param input A directory or an pdf file.
 * @param outputDir Output dir.
 * @param renderer The object responsible to render the skp object into pdf.
 */
static int process_input(const char* input, const SkString& outputDir,
                         SkPdfRenderer& renderer) {
    int failures = 0;
    if (sk_isdir(input)) {
        SkOSFile::Iter iter(input, PDF_FILE_EXTENSION);
        SkString inputFilename;
        while (iter.next(&inputFilename)) {
            SkString inputPath = SkOSPath::SkPathJoin(input, inputFilename.c_str());
            if (!process_pdf(inputPath, outputDir, renderer)) {
                ++failures;
            }
        }
    } else {
        SkString inputPath(input);
        if (!process_pdf(inputPath, outputDir, renderer)) {
            ++failures;
        }
    }
    return failures;
}

int tool_main(int argc, char** argv);
int tool_main(int argc, char** argv) {
    SkCommandLineFlags::SetUsage("Parse and Render .pdf files (pdf viewer).");
    SkCommandLineFlags::Parse(argc, argv);

    if (FLAGS_readPath.isEmpty()) {
        SkDebugf(".pdf files or directories are required.\n");
        exit(-1);
    }

    SkPdfRenderer renderer;

    SkString outputDir;
    if (FLAGS_writePath.count() == 1) {
        outputDir.set(FLAGS_writePath[0]);
    }

    int failures = 0;
    for (int i = 0; i < FLAGS_readPath.count(); i ++) {
        failures += process_input(FLAGS_readPath[i], outputDir, renderer);
        renderer.unload();
    }

    reportPdfRenderStats();

    if (failures != 0) {
        SkDebugf("Failed to render %i PDFs.\n", failures);
        return 1;
    }

    return 0;
}

#if !defined SK_BUILD_FOR_IOS
int main(int argc, char * const argv[]) {
    return tool_main(argc, (char**) argv);
}
#endif
<|endoftext|>