Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Util/include/Poco/Util/PropertyFileConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ class Util_API PropertyFileConfiguration: public MapConfiguration
/// A value can spread across multiple lines if the last character in a line (the character
/// immediately before the carriage return or line feed character) is a single backslash.
///
/// A line of the form
/// !include <path>
/// (where <path> is a relative or absolute file path) includes another properties file.
/// Relative paths are resolved relative to the directory of the including file.
///
/// Property names are case sensitive. Leading and trailing whitespace is
/// removed from both keys and values. A property name can neither contain
/// a colon ':' nor an equal sign '=' character.
Expand Down Expand Up @@ -88,7 +93,8 @@ class Util_API PropertyFileConfiguration: public MapConfiguration
~PropertyFileConfiguration() = default;

private:
void parseLine(std::istream& istr);
void loadStream(std::istream& istr, const std::string& basePath);
void parseLine(std::istream& istr, const std::string& basePath);
static int readChar(std::istream& istr);
};

Expand Down
50 changes: 37 additions & 13 deletions Util/src/PropertyFileConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,24 +46,32 @@ void PropertyFileConfiguration::load(std::istream& istr)
AbstractConfiguration::ScopedLock lock(*this);

clear();
while (!istr.eof())
{
if (istr.fail())
{
throw Poco::IOException("Broken input stream");
}
parseLine(istr);
}
loadStream(istr, "");
}


void PropertyFileConfiguration::load(const std::string& path)
{
Poco::Path p(path);
p.makeAbsolute();
std::string basePath = p.parent().toString();
Poco::FileInputStream istr(path);
if (istr.good())
load(istr);
else
if (!istr.good())
throw Poco::OpenFileException(path);
AbstractConfiguration::ScopedLock lock(*this);
clear();
loadStream(istr, basePath);
}


void PropertyFileConfiguration::loadStream(std::istream& istr, const std::string& basePath)
{
while (!istr.eof())
{
if (istr.fail())
throw Poco::IOException("Broken input stream");
parseLine(istr, basePath);
}
}


Expand Down Expand Up @@ -121,7 +129,7 @@ void PropertyFileConfiguration::save(const std::string& path) const
}


void PropertyFileConfiguration::parseLine(std::istream& istr)
void PropertyFileConfiguration::parseLine(std::istream& istr, const std::string& basePath)
{
static const int eof = std::char_traits<char>::eof();

Expand All @@ -131,7 +139,23 @@ void PropertyFileConfiguration::parseLine(std::istream& istr)
{
if (c == '#' || c == '!')
{
while (c != eof && c != '\n' && c != '\r') c = istr.get();
std::string line;
line += (char) c;
while (c != eof && c != '\n' && c != '\r') { c = istr.get(); if (c != eof && c != '\n' && c != '\r') line += (char) c; }

static const std::string includeDirective = "!include ";
if (line.size() > includeDirective.size() &&
line.compare(0, includeDirective.size(), includeDirective) == 0)
{
std::string includePath = Poco::trim(line.substr(includeDirective.size()));
Poco::Path p(includePath);
if (p.isRelative() && !basePath.empty())
p = Poco::Path(basePath).resolve(p);
Poco::FileInputStream includeIstr(p.toString());
if (!includeIstr.good())
throw Poco::OpenFileException(p.toString());
loadStream(includeIstr, p.parent().makeAbsolute().toString());
}
}
else
{
Expand Down
47 changes: 47 additions & 0 deletions Util/testsuite/src/PropertyFileConfigurationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include "Poco/Util/PropertyFileConfiguration.h"
#include "Poco/AutoPtr.h"
#include "Poco/Exception.h"
#include "Poco/TemporaryFile.h"
#include "Poco/FileStream.h"
#include <sstream>
#include <algorithm>

Expand Down Expand Up @@ -119,6 +121,50 @@ void PropertyFileConfigurationTest::testSave()
}


void PropertyFileConfigurationTest::testInclude()
{
// Write an included properties file
Poco::TemporaryFile includedFile;
{
Poco::FileOutputStream ostr(includedFile.path());
ostr << "included.prop1 = includedValue1\n";
ostr << "included.prop2 = includedValue2\n";
}

// Write a main properties file that includes the other
Poco::TemporaryFile mainFile;
{
Poco::FileOutputStream ostr(mainFile.path());
ostr << "main.prop = mainValue\n";
ostr << "!include " << includedFile.path() << "\n";
ostr << "main.prop2 = mainValue2\n";
}

AutoPtr<PropertyFileConfiguration> pConf = new PropertyFileConfiguration(mainFile.path());

assertTrue (pConf->getString("main.prop") == "mainValue");
assertTrue (pConf->getString("main.prop2") == "mainValue2");
assertTrue (pConf->getString("included.prop1") == "includedValue1");
assertTrue (pConf->getString("included.prop2") == "includedValue2");

// Non-existent include should throw
Poco::TemporaryFile mainFile2;
{
Poco::FileOutputStream ostr(mainFile2.path());
ostr << "prop = value\n";
ostr << "!include /nonexistent/path/to/file.properties\n";
}
try
{
AutoPtr<PropertyFileConfiguration> pConf2 = new PropertyFileConfiguration(mainFile2.path());
fail("must throw");
}
catch (Poco::FileException&)
{
}
}


AbstractConfiguration::Ptr PropertyFileConfigurationTest::allocConfiguration() const
{
return new PropertyFileConfiguration;
Expand All @@ -142,6 +188,7 @@ CppUnit::Test* PropertyFileConfigurationTest::suite()
AbstractConfigurationTest_addTests(pSuite, PropertyFileConfigurationTest);
CppUnit_addTest(pSuite, PropertyFileConfigurationTest, testLoad);
CppUnit_addTest(pSuite, PropertyFileConfigurationTest, testSave);
CppUnit_addTest(pSuite, PropertyFileConfigurationTest, testInclude);

return pSuite;
}
1 change: 1 addition & 0 deletions Util/testsuite/src/PropertyFileConfigurationTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class PropertyFileConfigurationTest: public AbstractConfigurationTest

void testLoad();
void testSave();
void testInclude();

void setUp();
void tearDown();
Expand Down
2 changes: 2 additions & 0 deletions XML/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ src/latin1tab.h
src/nametab.h
src/siphash.h
src/utf8tab.h
src/winconfig.h
src/xmlparse.c
src/xmlparse.cpp
src/xmlrole.c
src/xmlrole.h
Expand Down
Loading