commit 6c57007c8d4ff1159d89aae432c03ed38f84adae
parent 4df98b3f5fdf10d1312f35695a42b8a17cca1dd2
Author: cfillion <cfillion@users.noreply.github.com>
Date: Sun, 26 Jun 2016 14:54:34 -0400
extract integer array serializer
Diffstat:
5 files changed, 230 insertions(+), 61 deletions(-)
diff --git a/src/listview.cpp b/src/listview.cpp
@@ -20,7 +20,6 @@
#include "menu.hpp"
#include <boost/algorithm/string.hpp>
-#include <sstream>
#ifdef _WIN32
#include <commctrl.h>
@@ -28,12 +27,8 @@
using namespace std;
-static const unsigned short VERSION = 1;
-static const char FIELD_END = '\x20';
-static const char RECORD_END = ',';
-
ListView::ListView(const Columns &columns, HWND handle)
- : Control(handle), m_userVersion(0), m_sort(), m_defaultSort()
+ : Control(handle), m_sort(), m_defaultSort()
{
for(const Column &col : columns)
addColumn(col);
@@ -298,7 +293,7 @@ bool ListView::onContextMenu(HWND dialog, int x, int y)
#endif
if(point.y < headerHeight) {
- if(m_userVersion) // show menu only if header is customizable
+ if(m_serializer) // show menu only if header is customizable
headerMenu(x, y);
return true;
}
@@ -449,53 +444,34 @@ void ListView::resetColumns()
}
}
-bool ListView::restore(const string &data, const int userVersion)
+void ListView::restore(const string &input, const int userVersion)
{
- m_userVersion = userVersion; // for save(). also enables advanced customization
setExStyle(LVS_EX_HEADERDRAGDROP, true); // enable column reordering
+ const Serializer::Vector &data = m_serializer.read(input, userVersion);
if(data.empty())
- return false;
+ return;
- int col = -2;
+ int col = -1;
vector<int> order(columnCount());
- istringstream stream(data);
-
- while(true) {
- string line, first, second;
- getline(stream, line, RECORD_END);
- istringstream lineStream(line);
- getline(lineStream, first, FIELD_END);
- getline(lineStream, second, FIELD_END);
-
- int left, right;
-
- try {
- left = stoi(first.c_str());
- right = stoi(second.c_str());
- }
- catch(logic_error &) {
- return false; // data is invalid! aborting.
- }
+ for(const auto &rec : data) {
+ const int left = rec[0];
+ const int right = rec[1];
switch(col) {
- case -2: // version
- if(left != userVersion || right != VERSION)
- return false;
- break;
case -1: // sort
if(left < columnCount())
sortByColumn(left, right == 0 ? AscendingOrder : DescendingOrder, true);
break;
- default:
+ default: // column
order[col] = left;
// raw size should not go through adjustSize (via resizeColumn)
ListView_SetColumnWidth(handle(), col, right);
break;
}
- if(stream.eof() || ++col >= columnCount())
+ if(++col >= columnCount())
break;
}
@@ -504,37 +480,19 @@ bool ListView::restore(const string &data, const int userVersion)
order[col] = col;
ListView_SetColumnOrderArray(handle(), columnCount(), &order[0]);
-
- return true;
}
string ListView::save() const
{
+ const Sort sort = m_sort.value_or(Sort());
vector<int> order(columnCount());
ListView_GetColumnOrderArray(handle(), columnCount(), &order[0]);
- ostringstream stream;
-
- stream
- << m_userVersion << FIELD_END
- << VERSION << RECORD_END;
+ Serializer::Vector data;
+ data.push_back({sort.column, sort.order});
- Sort sort = m_sort.value_or(Sort());
- stream
- << sort.column << FIELD_END
- << sort.order << RECORD_END;
-
- int i = 0;
- while(true) {
- stream
- << order[i] << FIELD_END
- << columnWidth(i);
-
- if(++i < columnCount())
- stream << RECORD_END;
- else
- break;
- }
+ for(int i = 0; i < columnCount(); i++)
+ data.push_back({order[i], columnWidth(i)});
- return stream.str();
+ return m_serializer.write(data);
}
diff --git a/src/listview.hpp b/src/listview.hpp
@@ -26,6 +26,7 @@
#include <vector>
#include "encoding.hpp"
+#include "serializer.hpp"
class Menu;
@@ -83,7 +84,7 @@ public:
void sortByColumn(int index, SortOrder order = AscendingOrder, bool user = false);
void setSortCallback(int i, const SortCallback &cb) { m_sortFuncs[i] = cb; }
- bool restore(const std::string &, int userVersion);
+ void restore(const std::string &, int userVersion);
std::string save() const;
void resetColumns();
@@ -113,7 +114,7 @@ private:
int translateBack(int internalIndex) const;
void headerMenu(int x, int y);
- int m_userVersion;
+ Serializer m_serializer;
std::vector<Column> m_cols;
std::vector<Row> m_rows;
boost::optional<Sort> m_sort;
diff --git a/src/serializer.cpp b/src/serializer.cpp
@@ -0,0 +1,108 @@
+/* ReaPack: Package manager for REAPER
+ * Copyright (C) 2015-2016 Christian Fillion
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "serializer.hpp"
+
+#include <sstream>
+
+static const unsigned short VERSION = 1;
+static const char FIELD_END = '\x20';
+static const char RECORD_END = ',';
+
+using namespace std;
+
+auto Serializer::read(const string &input, const int userVersion) -> Vector
+{
+ m_userVersion = userVersion;
+
+ bool first = true;
+ istringstream stream(input);
+
+ Vector out;
+
+ while(!stream.eof()) {
+ string line;
+ getline(stream, line, RECORD_END);
+
+ istringstream lineStream(line);
+
+ Record rec;
+ for(size_t i = 0; i < rec.size(); i++) {
+ if(lineStream.eof())
+ return out;
+
+ string field;
+ getline(lineStream, field, FIELD_END);
+
+ int value;
+
+ try {
+ value = stoi(field.c_str());
+ }
+ catch(logic_error &) {
+ return out; // data is invalid! aborting.
+ }
+
+ rec[i] = value;
+ }
+
+ if(first) {
+ if(rec[0] != m_userVersion || rec[1] != VERSION)
+ return {};
+
+ first = false;
+ }
+ else
+ out.push_back(rec);
+ }
+
+ return out;
+}
+
+string Serializer::write(const Vector &data) const
+{
+ if(!m_userVersion)
+ return {};
+
+ ostringstream stream;
+
+ stream
+ << m_userVersion << FIELD_END
+ << VERSION << RECORD_END;
+
+ size_t i = 0;
+ while(true) {
+ const Record &rec = data[i];
+
+ size_t j = 0;
+ while(true) {
+ stream << rec[j];
+
+ if(++j < rec.size())
+ stream << FIELD_END;
+ else
+ break;
+ }
+
+ if(++i < data.size())
+ stream << RECORD_END;
+ else
+ break;
+ }
+
+ return stream.str();
+}
diff --git a/src/serializer.hpp b/src/serializer.hpp
@@ -0,0 +1,41 @@
+/* ReaPack: Package manager for REAPER
+ * Copyright (C) 2015-2016 Christian Fillion
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef REAPACK_SERIALIZER_HPP
+#define REAPACK_SERIALIZER_HPP
+
+#include <array>
+#include <string>
+#include <vector>
+
+class Serializer {
+public:
+ typedef std::array<int, 2> Record;
+ typedef std::vector<Record> Vector;
+
+ Serializer() : m_userVersion(0) {}
+ int userVersion() const { return m_userVersion; }
+ operator bool() const { return m_userVersion > 0; }
+
+ Vector read(const std::string &, int userVersion);
+ std::string write(const Vector &in) const;
+
+private:
+ int m_userVersion;
+};
+
+#endif
diff --git a/test/serializer.cpp b/test/serializer.cpp
@@ -0,0 +1,61 @@
+#include <catch.hpp>
+
+#include <serializer.hpp>
+
+static const char *M = "[serializer]";
+
+TEST_CASE("read from serialized data", M) {
+ Serializer s;
+ REQUIRE(s.userVersion() == 0);
+ REQUIRE_FALSE(s);
+
+ SECTION("valid") {
+ const auto &out = s.read("1 1,1 2,3 4,5 6", 1);
+ REQUIRE(out.size() == 3);
+ REQUIRE(out[0] == (Serializer::Record{1,2}));
+ REQUIRE(out[1] == (Serializer::Record{3,4}));
+ REQUIRE(out[2] == (Serializer::Record{5,6}));
+
+ REQUIRE(s.userVersion() == 1);
+ REQUIRE(s);
+ }
+
+ SECTION("wrong user version") {
+ const auto &out = s.read("1 1,1 2,3 4,5 6", 2);
+ REQUIRE(out.empty());
+ REQUIRE(s.userVersion() == 2);
+ REQUIRE(s);
+ }
+
+ SECTION("wrong data version") {
+ const auto &out = s.read("1 42,1 2,3 4,5 6", 1);
+ REQUIRE(out.empty());
+ REQUIRE(s.userVersion() == 1);
+ REQUIRE(s);
+ }
+
+ SECTION("not an integer") {
+ const auto &out = s.read("1 1,1 2,hello world,3 4", 1);
+ REQUIRE(out.size() == 1);
+ REQUIRE(out[0] == (Serializer::Record{1,2}));
+ }
+
+ SECTION("single field") {
+ const auto &out = s.read("1 1,1 2,3,4 5", 1);
+ REQUIRE(out.size() == 1);
+ REQUIRE(out[0] == (Serializer::Record{1,2}));
+ }
+
+ SECTION("empty string") {
+ const auto &out = s.read("", 1);
+ REQUIRE(out.empty());
+ }
+}
+
+TEST_CASE("write to string", M) {
+ Serializer s;
+ REQUIRE(s.write({{1, 2}}).empty()); // no user version set
+ s.read({}, 42);
+
+ REQUIRE(s.write({{1, 2}, {3, 4}}) == "42 1,1 2,3 4");
+}