Move into proper namespace

This commit is contained in:
TheAssassin
2018-11-09 01:56:05 +01:00
parent 93c947b56a
commit 4bb1ef124b
6 changed files with 345 additions and 315 deletions

View File

@@ -7,116 +7,124 @@
#include "desktopfileentry.h"
using boost::lexical_cast;
using namespace linuxdeploy::core::log;
class DesktopFileEntry::PrivateData {
public:
std::string key;
std::string value;
namespace linuxdeploy {
namespace core {
using namespace log;
namespace desktopfile {
class DesktopFileEntry::PrivateData {
public:
std::string key;
std::string value;
public:
void copyData(const std::shared_ptr<PrivateData>& other) {
key = other->key;
value = other->value;
}
public:
void copyData(const std::shared_ptr<PrivateData>& other) {
key = other->key;
value = other->value;
}
void assertValueNotEmpty() {
if (value.empty())
throw std::invalid_argument("value is empty");
void assertValueNotEmpty() {
if (value.empty())
throw std::invalid_argument("value is empty");
}
};
DesktopFileEntry::DesktopFileEntry() : d(new PrivateData) {}
DesktopFileEntry::DesktopFileEntry(std::string key, std::string value) : DesktopFileEntry() {
d->key = std::move(key);
d->value = std::move(value);
}
DesktopFileEntry::DesktopFileEntry(const DesktopFileEntry& other) : DesktopFileEntry() {
d->copyData(other.d);
}
DesktopFileEntry& DesktopFileEntry::operator=(const DesktopFileEntry& other) {
if (this != &other) {
d.reset(new PrivateData);
d->copyData(other.d);
}
return *this;
}
DesktopFileEntry& DesktopFileEntry::operator=(DesktopFileEntry&& other) noexcept {
if (this != &other) {
d = other.d;
other.d = nullptr;
}
return *this;
}
bool DesktopFileEntry::operator==(const DesktopFileEntry& other) const {
return d->key == other.d->key && d->value == other.d->value;
}
bool DesktopFileEntry::operator!=(const DesktopFileEntry& other) const {
return !operator==(other);
}
bool DesktopFileEntry::isEmpty() const {
return d->key.empty();
}
const std::string& DesktopFileEntry::key() const {
return d->key;
}
const std::string& DesktopFileEntry::value() const {
return d->value;
}
int DesktopFileEntry::asInt() const {
d->assertValueNotEmpty();
return lexical_cast<int>(value());
}
long DesktopFileEntry::asLong() const {
d->assertValueNotEmpty();
return lexical_cast<long>(value());
}
double DesktopFileEntry::asDouble() const {
d->assertValueNotEmpty();
return lexical_cast<double>(value());
}
std::vector<std::string> DesktopFileEntry::parseStringList() const {
const auto& value = this->value();
if (value.empty())
return {};
if (value.back() != ';')
ldLog() << LD_DEBUG << "desktop file string list does not end with semicolon:" << value
<< std::endl;
std::vector<std::string> list;
std::stringstream ss(value);
std::string currentVal;
while (std::getline(ss, currentVal, ';')) {
// the last value will be empty, as in desktop files, lists shall end with a semicolon
// therefore we skip all empty values (assuming that empty values in lists in desktop files don't make sense anyway)
if (!currentVal.empty())
list.emplace_back(currentVal);
}
return list;
}
DesktopFileEntry::operator std::string() const {
return value();
}
}
}
};
DesktopFileEntry::DesktopFileEntry() : d(new PrivateData) {}
DesktopFileEntry::DesktopFileEntry(std::string key, std::string value) : DesktopFileEntry() {
d->key = std::move(key);
d->value = std::move(value);
}
DesktopFileEntry::DesktopFileEntry(const DesktopFileEntry& other) : DesktopFileEntry() {
d->copyData(other.d);
}
DesktopFileEntry& DesktopFileEntry::operator=(const DesktopFileEntry& other) {
if (this != &other) {
d.reset(new PrivateData);
d->copyData(other.d);
}
return *this;
}
DesktopFileEntry& DesktopFileEntry::operator=(DesktopFileEntry&& other) noexcept {
if (this != &other) {
d = other.d;
other.d = nullptr;
}
return *this;
}
bool DesktopFileEntry::operator==(const DesktopFileEntry& other) const {
return d->key == other.d->key && d->value == other.d->value;
}
bool DesktopFileEntry::operator!=(const DesktopFileEntry& other) const {
return !operator==(other);
}
bool DesktopFileEntry::isEmpty() const {
return d->key.empty();
}
const std::string& DesktopFileEntry::key() const {
return d->key;
}
const std::string& DesktopFileEntry::value() const {
return d->value;
}
int DesktopFileEntry::asInt() const {
d->assertValueNotEmpty();
return lexical_cast<int>(value());
}
long DesktopFileEntry::asLong() const {
d->assertValueNotEmpty();
return lexical_cast<long>(value());
}
double DesktopFileEntry::asDouble() const {
d->assertValueNotEmpty();
return lexical_cast<double>(value());
}
std::vector<std::string> DesktopFileEntry::parseStringList() const {
const auto& value = this->value();
if (value.empty())
return {};
if (value.back() != ';')
ldLog() << LD_DEBUG << "desktop file string list does not end with semicolon:" << value << std::endl;
std::vector<std::string> list;
std::stringstream ss(value);
std::string currentVal;
while (std::getline(ss, currentVal, ';')) {
// the last value will be empty, as in desktop files, lists shall end with a semicolon
// therefore we skip all empty values (assuming that empty values in lists in desktop files don't make sense anyway)
if (!currentVal.empty())
list.emplace_back(currentVal);
}
return list;
}
DesktopFileEntry::operator std::string() const {
return value();
}

View File

@@ -7,62 +7,69 @@
// library headers
#include <boost/lexical_cast.hpp>
class DesktopFileEntry {
private:
// opaque data class pattern
class PrivateData;
std::shared_ptr<PrivateData> d;
namespace linuxdeploy {
namespace core {
namespace desktopfile {
class DesktopFileEntry {
private:
// opaque data class pattern
class PrivateData;
public:
// default constructor
DesktopFileEntry();
std::shared_ptr<PrivateData> d;
// construct from key and value
explicit DesktopFileEntry(std::string key, std::string value);
public:
// default constructor
DesktopFileEntry();
// copy constructor
DesktopFileEntry(const DesktopFileEntry& other);
// construct from key and value
explicit DesktopFileEntry(std::string key, std::string value);
// copy assignment constructor
DesktopFileEntry& operator=(const DesktopFileEntry& other);
// copy constructor
DesktopFileEntry(const DesktopFileEntry& other);
// move assignment operator
DesktopFileEntry& operator=(DesktopFileEntry&& other) noexcept;
// copy assignment constructor
DesktopFileEntry& operator=(const DesktopFileEntry& other);
// equality operator
bool operator==(const DesktopFileEntry& other) const;
// move assignment operator
DesktopFileEntry& operator=(DesktopFileEntry&& other) noexcept;
// inequality operator
bool operator!=(const DesktopFileEntry& other) const;
// equality operator
bool operator==(const DesktopFileEntry& other) const;
public:
// checks whether a key and value have been set
bool isEmpty() const;
// inequality operator
bool operator!=(const DesktopFileEntry& other) const;
// return entry's key
const std::string& key() const;
public:
// checks whether a key and value have been set
bool isEmpty() const;
// return entry's value
const std::string& value() const;
// return entry's key
const std::string& key() const;
public:
// allow conversion of entry to string
// returns value
explicit operator std::string() const;
// return entry's value
const std::string& value() const;
// convert value to integer
// throws boost::bad_lexical_cast in case of type errors
int asInt() const;
public:
// allow conversion of entry to string
// returns value
explicit operator std::string() const;
// convert value to long
// throws boost::bad_lexical_cast in case of type errors
long asLong() const;
// convert value to integer
// throws boost::bad_lexical_cast in case of type errors
int asInt() const;
// convert value to double
// throws boost::bad_lexical_cast in case of type errors
double asDouble() const;
// convert value to long
// throws boost::bad_lexical_cast in case of type errors
long asLong() const;
// split CSV list value into vector
// the separator used to split the string is a semicolon as per desktop file spec
std::vector<std::string> parseStringList() const;
};
// convert value to double
// throws boost::bad_lexical_cast in case of type errors
double asDouble() const;
// split CSV list value into vector
// the separator used to split the string is a semicolon as per desktop file spec
std::vector<std::string> parseStringList() const;
};
}
}
}

View File

@@ -11,151 +11,157 @@
namespace bf = boost::filesystem;
// describes a single section
typedef std::unordered_map<std::string, DesktopFileEntry> section_t;
namespace linuxdeploy {
namespace core {
namespace desktopfile {
// describes a single section
typedef std::unordered_map<std::string, DesktopFileEntry> section_t;
// describes all sections in the desktop file
typedef std::unordered_map<std::string, section_t> sections_t;
// describes all sections in the desktop file
typedef std::unordered_map<std::string, section_t> sections_t;
class DesktopFileReader::PrivateData {
public:
bf::path path;
sections_t sections;
class DesktopFileReader::PrivateData {
public:
bf::path path;
sections_t sections;
public:
bool isEmpty() {
return sections.empty();
}
void assertPathIsNotEmptyAndFileExists() {
if (path.empty())
throw std::invalid_argument("empty path is not permitted");
}
void copyData(const std::shared_ptr<PrivateData>& other) {
path = other->path;
}
void parse(std::istream& file) {
std::string line;
bool first = true;
std::string currentSectionName;
while (std::getline(file, line)) {
if (first) {
first = false;
// said to allow handling of UTF-16/32 documents, not entirely sure why
if (line[0] == static_cast<std::string::value_type>(0xEF))
{
line.erase(0, 3);
return;
public:
bool isEmpty() {
return sections.empty();
}
}
if (!line.empty()) {
auto len = line.length();
if (len > 0 && !((len >= 2 && (line[0] == '/' && line[1] == '/')) || (len >= 1 && line[0] == '#'))) {
if (line[0] == '[') {
// this line apparently introduces a new section
size_t length = len - 2;
auto title = line.substr(1, line.find(']') - 1);
void assertPathIsNotEmptyAndFileExists() {
if (path.empty())
throw std::invalid_argument("empty path is not permitted");
}
// set up the new section
sections.insert(std::make_pair(title, section_t()));
currentSectionName = std::move(title);
} else {
// we require at least one section to be present in the desktop file
if (currentSectionName.empty())
throw std::invalid_argument("No section in desktop file");
void copyData(const std::shared_ptr<PrivateData>& other) {
path = other->path;
}
// this line should be a normal key-value pair
std::string key = line.substr(0, line.find('='));
std::string value = line.substr(line.find('=') + 1, line.size());
void parse(std::istream& file) {
std::string line;
bool first = true;
// we can strip away any sort of leading or trailing whitespace safely
linuxdeploy::util::trim(key);
linuxdeploy::util::trim(value);
std::string currentSectionName;
// empty keys are not allowed for obvious reasons
if (key.empty())
throw std::invalid_argument("Empty keys are not allowed");
while (std::getline(file, line)) {
if (first) {
first = false;
// said to allow handling of UTF-16/32 documents, not entirely sure why
if (line[0] == static_cast<std::string::value_type>(0xEF)) {
line.erase(0, 3);
return;
}
}
// who are we to judge whether empty values are an issue
// that'd require checking the key names and implementing checks per key according to the
// freedesktop.org spec
sections[currentSectionName][key] = DesktopFileEntry(key, value);
if (!line.empty()) {
auto len = line.length();
if (len > 0 &&
!((len >= 2 && (line[0] == '/' && line[1] == '/')) || (len >= 1 && line[0] == '#'))) {
if (line[0] == '[') {
// this line apparently introduces a new section
size_t length = len - 2;
auto title = line.substr(1, line.find(']') - 1);
// set up the new section
sections.insert(std::make_pair(title, section_t()));
currentSectionName = std::move(title);
} else {
// we require at least one section to be present in the desktop file
if (currentSectionName.empty())
throw std::invalid_argument("No section in desktop file");
// this line should be a normal key-value pair
std::string key = line.substr(0, line.find('='));
std::string value = line.substr(line.find('=') + 1, line.size());
// we can strip away any sort of leading or trailing whitespace safely
linuxdeploy::util::trim(key);
linuxdeploy::util::trim(value);
// empty keys are not allowed for obvious reasons
if (key.empty())
throw std::invalid_argument("Empty keys are not allowed");
// who are we to judge whether empty values are an issue
// that'd require checking the key names and implementing checks per key according to the
// freedesktop.org spec
sections[currentSectionName][key] = DesktopFileEntry(key, value);
}
}
}
}
}
};
DesktopFileReader::DesktopFileReader() : d(new PrivateData) {}
DesktopFileReader::DesktopFileReader(boost::filesystem::path path) : DesktopFileReader() {
d->path = std::move(path);
d->assertPathIsNotEmptyAndFileExists();
std::ifstream ifs(d->path.string());
if (!ifs)
throw std::invalid_argument("could not open file: " + d->path.string());
d->parse(ifs);
}
DesktopFileReader::DesktopFileReader(std::istream& is) : DesktopFileReader() {
d->parse(is);
}
DesktopFileReader::DesktopFileReader(const DesktopFileReader& other) : DesktopFileReader() {
d->copyData(other.d);
}
DesktopFileReader& DesktopFileReader::operator=(const DesktopFileReader& other) {
if (this != &other) {
// set up a new instance of PrivateData, and copy data over from other object
d.reset(new PrivateData);
d->copyData(other.d);
}
return *this;
}
DesktopFileReader& DesktopFileReader::operator=(DesktopFileReader&& other) noexcept {
if (this != &other) {
// move other object's data into this one, and remove reference there
d = other.d;
other.d = nullptr;
}
return *this;
}
bool DesktopFileReader::isEmpty() const {
return d->isEmpty();
}
bool DesktopFileReader::operator==(const DesktopFileReader& other) const {
return d->path == other.d->path;
}
bool DesktopFileReader::operator!=(const DesktopFileReader& other) const {
return !operator==(other);
}
boost::filesystem::path DesktopFileReader::path() const {
return d->path;
}
section_t DesktopFileReader::operator[](const std::string& name) {
auto it = d->sections.find(name);
// the map would lazy-initialize a new entry in case the section doesn't exist
// therefore explicitly checking whether the section exists, throwing an exception in case it does not
if (it == d->sections.end())
throw std::range_error("could not find section " + name);
return it->second;
}
}
}
};
DesktopFileReader::DesktopFileReader() : d(new PrivateData) {}
DesktopFileReader::DesktopFileReader(boost::filesystem::path path) : DesktopFileReader() {
d->path = std::move(path);
d->assertPathIsNotEmptyAndFileExists();
std::ifstream ifs(d->path.string());
if (!ifs)
throw std::invalid_argument("could not open file: " + d->path.string());
d->parse(ifs);
}
DesktopFileReader::DesktopFileReader(std::istream& is) : DesktopFileReader() {
d->parse(is);
}
DesktopFileReader::DesktopFileReader(const DesktopFileReader& other) : DesktopFileReader() {
d->copyData(other.d);
}
DesktopFileReader& DesktopFileReader::operator=(const DesktopFileReader& other) {
if (this != &other) {
// set up a new instance of PrivateData, and copy data over from other object
d.reset(new PrivateData);
d->copyData(other.d);
}
return *this;
}
DesktopFileReader& DesktopFileReader::operator=(DesktopFileReader&& other) noexcept {
if (this != &other) {
// move other object's data into this one, and remove reference there
d = other.d;
other.d = nullptr;
}
return *this;
}
bool DesktopFileReader::isEmpty() const {
return d->isEmpty();
}
bool DesktopFileReader::operator==(const DesktopFileReader& other) const {
return d->path == other.d->path;
}
bool DesktopFileReader::operator!=(const DesktopFileReader& other) const {
return !operator==(other);
}
boost::filesystem::path DesktopFileReader::path() const {
return d->path;
}
section_t DesktopFileReader::operator[](const std::string& name) {
auto it = d->sections.find(name);
// the map would lazy-initialize a new entry in case the section doesn't exist
// therefore explicitly checking whether the section exists, throwing an exception in case it does not
if (it == d->sections.end())
throw std::range_error("could not find section " + name);
return it->second;
}

View File

@@ -11,45 +11,52 @@
// local includes
#include "desktopfileentry.h"
class DesktopFileReader {
private:
// opaque data class pattern
class PrivateData;
std::shared_ptr<PrivateData> d;
namespace linuxdeploy {
namespace core {
namespace desktopfile {
class DesktopFileReader {
private:
// opaque data class pattern
class PrivateData;
public:
// default constructor
DesktopFileReader();
std::shared_ptr<PrivateData> d;
// construct from path
explicit DesktopFileReader(boost::filesystem::path path);
public:
// default constructor
DesktopFileReader();
// construct from existing istream
explicit DesktopFileReader(std::istream& is);
// construct from path
explicit DesktopFileReader(boost::filesystem::path path);
// copy constructor
DesktopFileReader(const DesktopFileReader& other);
// construct from existing istream
explicit DesktopFileReader(std::istream& is);
// copy assignment constructor
DesktopFileReader& operator=(const DesktopFileReader& other);
// copy constructor
DesktopFileReader(const DesktopFileReader& other);
// move assignment operator
DesktopFileReader& operator=(DesktopFileReader&& other) noexcept;
// copy assignment constructor
DesktopFileReader& operator=(const DesktopFileReader& other);
// equality operator
bool operator==(const DesktopFileReader& other) const;
// move assignment operator
DesktopFileReader& operator=(DesktopFileReader&& other) noexcept;
// inequality operator
bool operator!=(const DesktopFileReader& other) const;
// equality operator
bool operator==(const DesktopFileReader& other) const;
public:
// checks whether parsed data is available
bool isEmpty() const;
// inequality operator
bool operator!=(const DesktopFileReader& other) const;
// returns desktop file path
boost::filesystem::path path() const;
public:
// checks whether parsed data is available
bool isEmpty() const;
// get a specific section from the parsed data
// throws std::range_error if section does not exist
std::unordered_map<std::string, DesktopFileEntry> operator[](const std::string& name);
};
// returns desktop file path
boost::filesystem::path path() const;
// get a specific section from the parsed data
// throws std::range_error if section does not exist
std::unordered_map<std::string, DesktopFileEntry> operator[](const std::string& name);
};
}
}
}

View File

@@ -7,6 +7,7 @@
#include "../../src/core/desktopfileentry.h"
using boost::bad_lexical_cast;
using namespace linuxdeploy::core::desktopfile;
namespace bf = boost::filesystem;

View File

@@ -5,6 +5,7 @@
// local headers
#include "../../src/core/desktopfilereader.h"
using namespace linuxdeploy::core::desktopfile;
namespace bf = boost::filesystem;
class DesktopFileReaderFixture : public ::testing::Test {