pikotools/space/space.h

568 lines
16 KiB
C++

/*
* This file is a part of PikoTools
* and is distributed under the (new) BSD licence.
* Author: Tomasz Sowa <t.sowa@ttmath.org>
*/
/*
* Copyright (c) 2010-2018, Tomasz Sowa
* All rights reserved.
*
* 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 Tomasz Sowa nor the names of contributors to this
* project 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.
*/
#ifndef headerfile_picotools_space_space
#define headerfile_picotools_space_space
#include <string>
#include <vector>
#include <map>
#include "textstream/types.h"
namespace PT
{
/*
A parser for parsing config files.
A config file can look like this:
variable1 = value 1
variable2 = " value 2 "
variable3 = (value 1, value 2)
variable4 = (" value 1 " , "value2", value 3)
sample of use:
SpaceParser parser;
parser.Parse("/path/to/config");
if( parser.status == SpaceParser::ok )
{
// the whole config we have in parser.table
}
config syntax:
option = list
list can consists of any number of items, if you're using more than one item you should
use brackets ()
for one item the brackets can be ommited:
option = value
white characters at the beginning of the value (and at the end) will be trimmed,
or you can use quotes:
option = "value"
option2 = "value with spaces at the end "
the form without quotes:
option = value
should be written in one line, so this is not allowed:
option =
value
you can use a new line characters only between brackets and quotes:
option = "this is
a multiline string"
option = ( value1,
value2 )
but there is one requirement: the first character " or ( should be in the same line,
so this is not allowed
option =
"this is wrong"
but this is ok:
option = "
that is ok"
empty lists:
option = ()
this creates an empty list: parser.table['option'].empty() == true
option =
this creates an empty list too (the same as previously)
option = ""
but this doesn't create an empty list, it creates a list with one (empty) item
commentaries:
# this is a commentary (until the end of the line)
option = value # this is a commentary too
commentaries are treated as white characters, other example:
option = ( # this is my list
"value 1" # this is a value one
value 2 # and this is a value two
) # end of my list
overwriting:
option1 = some value
option1 = other value
# always the last option is used so option1 is "other value"
list delimiter:
option1 = (value1, value2, value3)
option2 = ("value1", "value2", "value3")
above we're using a comma ',' as a list delimiter but when using quotes (second line)
the commas can be omitted:
option2 = ("value1" "value2" "value3")
white characters:
the name of an option cannot consist of white characters
some option = value # this is wrong
some_option = value # this is ok
which characters are allowed in an option name is defined by IsVariableChar() method
you can use white characters in values
option = value with spaces or tabs
white characters at the beginning and at the end will be trimmed,
so if you want them use quotes:
option = " other value with spaces "
special characters in quoted strings:
option = "this is a string with \" a quote inside"
the option will be: this is a string with " a quote inside
\\ - means one \
basically: \char produces char
so:
"\a" gives "a"
"\\" gives "\"
"\Z" gives "Z" and so on
you can call UseEscapeChar(false) to turn this off
*/
class Space
{
public:
/*
this is the table which represents your config file
in the Table map: the first (key) is your 'option' and the second is 'list'
*/
typedef std::vector<std::wstring> Value;
typedef std::map<std::wstring, Value> Table;
Space();
~Space();
Space(const Space & s);
Space & operator=(const Space & s);
// IMPROVE ME
// add move cctor
void Clear();
/*
returns true if such an option has 'value'
useful when testing lists (they don't have to be copied out)
*/
bool HasValue(const wchar_t * name, const wchar_t * value);
bool HasValue(const wchar_t * name, const std::wstring & value);
bool HasValue(const std::wstring & name, const wchar_t * value);
bool HasValue(const std::wstring & name, const std::wstring & value);
/*
*
* methods for getting/finding a value
*
*
*/
/*
*
* their working in O(log)
* can return a null pointer
*
*/
Value * GetValue(const wchar_t * name);
Value * GetValue(const std::wstring & name);
const Value * GetValue(const wchar_t * name) const;
const Value * GetValue(const std::wstring & name) const;
// O(n) complexity
Value * GetValueNoCase(const wchar_t * name);
Value * GetValueNoCase(const std::wstring & name);
const Value * GetValueNoCase(const wchar_t * name) const;
const Value * GetValueNoCase(const std::wstring & name) const;
// they can return a null pointer if there is not such a 'name'
std::wstring * GetFirstValue(const wchar_t * name);
std::wstring * GetFirstValue(const std::wstring & name);
const std::wstring * GetFirstValue(const wchar_t * name) const;
const std::wstring * GetFirstValue(const std::wstring & name) const;
/*
those methods are used to extract information from space.table
as a parameter they take the name of an option
and a default value (if there is no such a parameter),
they return appropriate value (either text, int or boolean)
(in lists they return the first item if exists)
when calling Text(...) and AText(...) you should copy the object to whom a reference is returned
it will be cleared in a next call to one of these methods (as well to Int() Size() and Bool())
AText(...) always returns a reference to UTF-8 string
*/
//std::wstring Text(const wchar_t * name) const;
std::wstring Text(const wchar_t * name, const wchar_t * def = 0) const;
std::wstring Text(const std::wstring & name, const wchar_t * def = 0) const;
std::wstring Text(const std::wstring & name, const std::wstring & def) const;
// returns a reference
// if there is no such an option then a new one (def value) is inserted
//std::wstring & TextRef(const wchar_t * name);
std::wstring & TextRef(const wchar_t * name, const wchar_t * def = 0);
std::wstring & TextRef(const std::wstring & name, const wchar_t * def = 0);
std::wstring & TextRef(const std::wstring & name, const std::wstring & def);
// returns UTF-8 string
//std::string TextA(const wchar_t * name) const;
std::string TextA(const wchar_t * name, const char * def) const;
std::string TextA(const std::wstring & name, const char * def) const;
std::string TextA(const std::wstring & name, const std::string & def) const;
std::string TextA(const wchar_t * name, const wchar_t * def) const;
std::string TextA(const std::wstring & name, const wchar_t * def) const;
std::string TextA(const std::wstring & name, const std::wstring & def) const;
int Int(const wchar_t * name, int def = 0) const;
int Int(const std::wstring & name, int def = 0) const;
unsigned int UInt(const wchar_t * name, unsigned int def = 0) const;
unsigned int UInt(const std::wstring & name, unsigned int def = 0) const;
long Long(const wchar_t * name, long def = 0) const;
long Long(const std::wstring & name, long def = 0) const;
unsigned long ULong(const wchar_t * name, unsigned long def = 0) const;
unsigned long ULong(const std::wstring & name, unsigned long def = 0) const;
long long LongLong(const wchar_t * name, long long def = 0) const;
long long LongLong(const std::wstring & name, long long def = 0) const;
unsigned long long ULongLong(const wchar_t * name, unsigned long long def = 0) const;
unsigned long long ULongLong(const std::wstring & name, unsigned long long def = 0) const;
size_t Size(const wchar_t * name, size_t def = 0) const;
size_t Size(const std::wstring & name, size_t def = 0) const;
bool Bool(const wchar_t * name, bool def = false) const;
bool Bool(const std::wstring & name, bool def = false) const;
/*
*
* methods for adding a new value
*
*
*/
std::wstring & FindAdd(const wchar_t * name);
std::wstring & FindAdd(const std::wstring & name);
std::wstring & FindAdd(const WTextStream & name);
std::wstring & Add(const wchar_t * name, bool value, bool replace_existing = true);
std::wstring & Add(const std::wstring & name, bool value, bool replace_existing = true);
std::wstring & Add(const wchar_t * name, int value, bool replace_existing = true);
std::wstring & Add(const std::wstring & name, int value, bool replace_existing = true);
std::wstring & Add(const wchar_t * name, long value, bool replace_existing = true);
std::wstring & Add(const std::wstring & name, long value, bool replace_existing = true);
std::wstring & Add(const wchar_t * name, size_t value, bool replace_existing = true);
std::wstring & Add(const std::wstring & name, size_t value, bool replace_existing = true);
std::wstring & Add(const std::wstring & name, const std::wstring & value, bool replace_existing = true);
std::wstring & Add(const std::wstring & name, const wchar_t * value, bool replace_existing = true);
std::wstring & Add(const wchar_t * name, const wchar_t * value, bool replace_existing = true);
std::wstring & Add(const wchar_t * name, const std::wstring & value, bool replace_existing = true);
std::wstring & Add(const wchar_t * name, const WTextStream & value, bool replace_existing = true);
std::wstring & Add(const std::wstring & name, const WTextStream & value, bool replace_existing = true);
std::wstring & Add(const WTextStream & name, const WTextStream & value, bool replace_existing = true);
void Remove(const wchar_t * name);
void Remove(const std::wstring & name);
Space & AddSpace(const wchar_t * name);
Space & AddSpace(const std::wstring & name);
// looking for the first space with the specified name
// if there is not such a space those methods return a null pointer
Space * FindSpace(const wchar_t * name);
Space * FindSpace(const std::wstring & name);
// looking for the first space with the specified name
// if there is not such a space then this methods adds such a space
Space & FindAddSpace(const wchar_t * name);
Space & FindAddSpace(const std::wstring & name);
void RemoveSpace(const wchar_t * name);
void RemoveSpace(const std::wstring & name);
void RemoveSpace(size_t child_index);
/*
*
*
raw access to the parsed values
*
*
*/
std::wstring name; // space name
Table table; // std::map<std::wstring, std::vector<std::wstring> >
// childs
typedef std::vector<Space*> Spaces;
Spaces spaces;
// a parent space
// null means a root space
Space * parent;
/*
those methods are used to extract lists
return true if such an option exists (but value can be an empty list)
*/
bool ListText(const wchar_t * name, std::vector<std::wstring> & list);
bool ListText(const std::wstring & name, std::vector<std::wstring> & list);
/*
serialize the content
*/
template<class Stream>
void Serialize(Stream & out, bool use_indents = false, bool use_comments = false, int level = 0) const;
template<class Stream>
void SerializeTableMulti(Stream & out, bool use_indents, int level) const;
template<class Stream, class StringType>
static void PrintValue(Stream & out, const StringType & str, bool use_quote = true);
template<class Stream>
static void PrintKey(Stream & out, const std::wstring & str);
template<class Stream>
static void PrintLevel(Stream & out, bool use_indents, int level);
private:
mutable std::wstring tmp_name;
std::wstring tmp_value;
std::wstring tmp_value_text;
std::string tmp_value_text_ascii;
static unsigned int ToUInt(const std::wstring & value);
static int ToInt(const std::wstring & value);
static unsigned long ToULong(const std::wstring & value);
static long ToLong(const std::wstring & value);
static unsigned long long ToULongLong(const std::wstring & value);
static long long ToLongLong(const std::wstring & value);
static size_t ToSize(const std::wstring & value);
static bool ToBool(const std::wstring & value);
static bool IsWhite(int c);
static bool HasWhite(const std::wstring & str);
};
template<class Stream>
void Space::PrintLevel(Stream & out, bool use_indents, int level)
{
if( use_indents )
{
for(int i=0 ; i<level ; ++i)
out << ' ';
}
}
template<class Stream, class StringType>
void Space::PrintValue(Stream & out, const StringType & str, bool use_quote)
{
if( use_quote )
out << '\"';
for(size_t i=0 ; i<str.size() ; ++i)
{
switch(str[i])
{
case 0: out << '\\'; out << '0'; break;
case '\r': out << '\\'; out << 'r'; break;
case '\n': out << '\\'; out << 'n'; break;
case '\\': out << '\\'; out << '\\'; break;
case '"': out << '\\'; out << '\"'; break;
case '(': out << '\\'; out << '('; break;
case ')': out << '\\'; out << ')'; break;
case '=': out << '\\'; out << '='; break;
default:
out << str[i];
}
}
if( use_quote )
out << '\"';
}
template<class Stream>
void Space::PrintKey(Stream & out, const std::wstring & str)
{
bool use_quote = false;
// CHECK ME
// HasWhite doesn't take a new line into account, is it correct to use it here?
if( str.empty() || HasWhite(str) )
use_quote = true;
PrintValue(out, str, use_quote);
}
template<class Stream>
void Space::SerializeTableMulti(Stream & out, bool use_indents, int level) const
{
Table::const_iterator i2;
size_t v;
for(i2 = table.begin() ; i2 != table.end() ; ++i2)
{
PrintLevel(out, use_indents, level);
PrintKey(out, i2->first);
out << L" = ";
if( i2->second.size() != 1 )
out << '(';
for(v = 0 ; v < i2->second.size() ; ++v)
{
if( v > 0 )
PrintLevel(out, use_indents, level + i2->first.size() + 3);
PrintValue(out, i2->second[v]);
if( v + 1 < i2->second.size() )
out << '\n';
}
if( i2->second.size() != 1 )
out << ')';
out << '\n';
}
}
template<class Stream>
void Space::Serialize(Stream & out, bool use_indents, bool use_comments, int level) const
{
if( level > 0 )
{
out << '\n';
PrintLevel(out, use_indents, level);
if( !name.empty() )
{
PrintKey(out, name);
out << ' ';
}
out << L"(\n";
if( use_comments )
{
PrintLevel(out, use_indents, level);
out << L"# space level " << level << '\n';
}
}
SerializeTableMulti(out, use_indents, level);
for(size_t i=0 ; i<spaces.size() ; ++i)
spaces[i]->Serialize(out, use_indents, use_comments, level+1);
if( level > 0 )
{
PrintLevel(out, use_indents, level);
out << ')';
if( use_comments )
{
if( name.empty() )
out << L" # end of unnamed space";
else
out << L" # end of space: " << name;
out << L" (level " << level << L")";
}
out << '\n';
}
}
} // namespace
#endif