syncevolution/src/syncevo/SingleFileConfigTree.cpp

259 lines
8.2 KiB
C++

/*
* Copyright (C) 2008-2009 Patrick Ohly <patrick.ohly@gmx.de>
*
* This library 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 2.1 of the License, or (at your option) version 3.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <config.h>
#include "test.h"
#include <syncevo/SingleFileConfigTree.h>
#include <syncevo/StringDataBlob.h>
#include <syncevo/FileDataBlob.h>
#include <syncevo/IniConfigNode.h>
#include <syncevo/util.h>
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
#include <syncevo/declarations.h>
SE_BEGIN_CXX
using namespace std;
SingleFileConfigTree::SingleFileConfigTree(const boost::shared_ptr<DataBlob> &data) :
m_data(data)
{
readFile();
}
SingleFileConfigTree::SingleFileConfigTree(const string &fullpath) :
m_data(new FileDataBlob(fullpath, true))
{
readFile();
}
boost::shared_ptr<ConfigNode> SingleFileConfigTree::open(const string &filename)
{
string normalized = normalizePath(string("/") + filename);
boost::shared_ptr<ConfigNode> &entry = m_nodes[normalized];
if (entry) {
return entry;
}
string name = m_data->getName() + " - " + normalized;
boost::shared_ptr<DataBlob> data;
BOOST_FOREACH(const FileContent_t::value_type &file, m_content) {
if (file.first == normalized) {
data.reset(new StringDataBlob(name, file.second, true));
break;
}
}
if (!data) {
/*
* creating new files not supported, would need support for detecting
* StringDataBlob::write()
*/
data.reset(new StringDataBlob(name, boost::shared_ptr<std::string>(), true));
}
entry.reset(new IniFileConfigNode(data));
return entry;
}
void SingleFileConfigTree::flush()
{
// not implemented, cannot write anyway
}
void SingleFileConfigTree::reload()
{
SE_THROW("SingleFileConfigTree::reload() not implemented");
}
void SingleFileConfigTree::remove(const string &path)
{
SE_THROW("internal error: SingleFileConfigTree::remove() called");
}
void SingleFileConfigTree::reset()
{
m_nodes.clear();
readFile();
}
boost::shared_ptr<ConfigNode> SingleFileConfigTree::open(const string &path,
PropertyType type,
const string &otherId)
{
string fullpath = path;
if (!fullpath.empty()) {
fullpath += "/";
}
switch (type) {
case visible:
fullpath += "config.ini";
break;
case hidden:
fullpath += ".internal.ini";
break;
case other:
fullpath += ".other.ini";
break;
case server:
fullpath += ".server.ini";
break;
}
return open(fullpath);
}
boost::shared_ptr<ConfigNode> SingleFileConfigTree::add(const string &path,
const boost::shared_ptr<ConfigNode> &bode)
{
SE_THROW("SingleFileConfigTree::add() not supported");
}
static void checkChild(const string &normalized,
const string &node,
set<string> &subdirs)
{
if (boost::starts_with(node, normalized)) {
string remainder = node.substr(normalized.size());
size_t offset = remainder.find('/');
if (offset != remainder.npos) {
// only directories underneath path matter
subdirs.insert(remainder.substr(0, offset));
}
}
}
list<string> SingleFileConfigTree::getChildren(const string &path)
{
set<string> subdirs;
string normalized = normalizePath(string("/") + path);
if (normalized != "/") {
normalized += "/";
}
// must check both actual files as well as unsaved nodes
BOOST_FOREACH(const FileContent_t::value_type &file, m_content) {
checkChild(normalized, file.first, subdirs);
}
BOOST_FOREACH(const NodeCache_t::value_type &file, m_nodes) {
checkChild(normalized, file.first, subdirs);
}
list<string> result;
BOOST_FOREACH(const string &dir, subdirs) {
result.push_back(dir);
}
return result;
}
void SingleFileConfigTree::readFile()
{
boost::shared_ptr<istream> in(m_data->read());
boost::shared_ptr<string> content;
string line;
m_content.clear();
while (getline(*in, line)) {
if (boost::starts_with(line, "=== ") &&
boost::ends_with(line, " ===")) {
string name = line.substr(4, line.size() - 8);
name = normalizePath(string("/") + name);
content.reset(new string);
m_content[name] = content;
} else if (content) {
(*content) += line;
(*content) += "\n";
}
}
}
#ifdef ENABLE_UNIT_TESTS
class SingleIniTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(SingleIniTest);
CPPUNIT_TEST(simple);
CPPUNIT_TEST_SUITE_END();
void simple() {
boost::shared_ptr<string> data(new string);
data->assign("# comment\n"
"# foo\n"
"=== foo/config.ini ===\n"
"foo = bar\n"
"foo2 = bar2\n"
"=== foo/.config.ini ===\n"
"foo_internal = bar_internal\n"
"foo2_internal = bar2_internal\n"
"=== /bar/.internal.ini ===\n"
"bar = foo\n"
"=== sources/addressbook/config.ini ===\n"
"=== sources/calendar/config.ini ===\n"
"evolutionsource = Personal\n");
boost::shared_ptr<DataBlob> blob(new StringDataBlob("test", data, true));
SingleFileConfigTree tree(blob);
boost::shared_ptr<ConfigNode> node;
node = tree.open("foo/config.ini");
CPPUNIT_ASSERT(node);
CPPUNIT_ASSERT(node->exists());
CPPUNIT_ASSERT_EQUAL(string("test - /foo/config.ini"), node->getName());
CPPUNIT_ASSERT(node->readProperty("foo").wasSet());
CPPUNIT_ASSERT_EQUAL(string("bar"), node->readProperty("foo").get());
CPPUNIT_ASSERT_EQUAL(string("bar2"), node->readProperty("foo2").get());
CPPUNIT_ASSERT(node->readProperty("foo2").wasSet());
CPPUNIT_ASSERT_EQUAL(string(""), node->readProperty("no_such_bar").get());
CPPUNIT_ASSERT(!node->readProperty("no_such_bar").wasSet());
node = tree.open("/foo/config.ini");
CPPUNIT_ASSERT(node);
CPPUNIT_ASSERT(node->exists());
node = tree.open("foo//.config.ini");
CPPUNIT_ASSERT(node);
CPPUNIT_ASSERT(node->exists());
CPPUNIT_ASSERT_EQUAL(string("bar_internal"), node->readProperty("foo_internal").get());
CPPUNIT_ASSERT_EQUAL(string("bar2_internal"), node->readProperty("foo2_internal").get());
node = tree.open("bar///./.internal.ini");
CPPUNIT_ASSERT(node);
CPPUNIT_ASSERT(node->exists());
CPPUNIT_ASSERT_EQUAL(string("foo"), node->readProperty("bar").get());
node = tree.open("sources/addressbook/config.ini");
CPPUNIT_ASSERT(node);
CPPUNIT_ASSERT(node->exists());
node = tree.open("sources/calendar/config.ini");
CPPUNIT_ASSERT(node);
CPPUNIT_ASSERT(node->exists());
CPPUNIT_ASSERT_EQUAL(string("Personal"), node->readProperty("evolutionsource").get());
node = tree.open("no-such-source/config.ini");
CPPUNIT_ASSERT(node);
CPPUNIT_ASSERT(!node->exists());
list<string> dirs = tree.getChildren("");
CPPUNIT_ASSERT_EQUAL(string("bar|foo|no-such-source|sources"), boost::join(dirs, "|"));
dirs = tree.getChildren("sources/");
CPPUNIT_ASSERT_EQUAL(string("addressbook|calendar"), boost::join(dirs, "|"));
}
};
SYNCEVOLUTION_TEST_SUITE_REGISTRATION(SingleIniTest);
#endif // ENABLE_UNIT_TESTS
SE_END_CXX