wesnoth/src/config.hpp
2010-03-27 18:49:14 +00:00

417 lines
13 KiB
C++

/* $Id$ */
/*
Copyright (C) 2003 - 2010 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file config.hpp
* Definitions for the interface to Wesnoth Markup Language (WML).
*
* This module defines the interface to Wesnoth Markup Language (WML). WML is
* a simple hierarchical text-based file format. The format is defined in
* Wiki, under BuildingScenariosWML
*
* All configuration files are stored in this format, and data is sent across
* the network in this format. It is thus used extensively throughout the
* game.
*/
#ifndef CONFIG_HPP_INCLUDED
#define CONFIG_HPP_INCLUDED
#include "global.hpp"
#include <map>
#include <ostream>
#include <string>
#include <vector>
#include "game_errors.hpp"
#include "tstring.hpp"
#include "shared_string.hpp"
typedef std::map<std::string, t_string> string_map;
class config;
bool operator==(const config &, const config &);
inline bool operator!=(const config &a, const config &b) { return !operator==(a, b); }
std::ostream &operator << (std::ostream &, const config &);
/** A config object defines a single node in a WML file, with access to child nodes. */
class config
{
friend bool operator==(const config& a, const config& b);
static config invalid;
/**
* Raises an exception if @a this is not valid.
*/
void check_valid() const;
/**
* Raises an exception if @a this or @a cfg is not valid.
*/
void check_valid(const config &cfg) const;
struct safe_bool_impl { void nonnull() {} };
/**
* Used as the return type of the conversion operator for boolean contexts.
* Needed, since the compiler would otherwise consider the following
* conversion (C legacy): cfg["abc"] -> "abc"[bool(cfg)] -> 'b'
*/
typedef void (safe_bool_impl::*safe_bool)();
public:
// Create an empty node.
config();
config(const config& cfg);
/**
* Creates a config object with an empty child of name @a child.
*/
explicit config(const std::string &child);
~config();
config& operator=(const config& cfg);
operator safe_bool() const
{ return this != &invalid ? &safe_bool_impl::nonnull : 0; }
typedef std::vector<config*> child_list;
typedef std::map<std::string,child_list> child_map;
struct const_child_iterator;
struct child_iterator
{
typedef config value_type;
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
typedef config *pointer;
typedef config &reference;
typedef child_list::iterator Itor;
explicit child_iterator(const Itor &i): i_(i) {}
child_iterator &operator++() { ++i_; return *this; }
child_iterator operator++(int) { return child_iterator(i_++); }
config &operator*() const { return **i_; }
config *operator->() const { return &**i_; }
bool operator==(const child_iterator &i) const { return i_ == i.i_; }
bool operator!=(const child_iterator &i) const { return i_ != i.i_; }
private:
Itor i_;
friend struct const_child_iterator;
};
struct const_child_iterator
{
typedef config value_type;
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
typedef const config *pointer;
typedef const config &reference;
typedef child_list::const_iterator Itor;
explicit const_child_iterator(const Itor &i): i_(i) {}
const_child_iterator(const child_iterator &i): i_(i.i_) {}
const_child_iterator &operator++() { ++i_; return *this; }
const_child_iterator operator++(int) { return const_child_iterator(i_++); }
const config &operator*() const { return **i_; }
const config *operator->() const { return &**i_; }
bool operator==(const const_child_iterator &i) const { return i_ == i.i_; }
bool operator!=(const const_child_iterator &i) const { return i_ != i.i_; }
private:
Itor i_;
};
typedef std::pair<child_iterator,child_iterator> child_itors;
typedef std::pair<const_child_iterator,const_child_iterator> const_child_itors;
typedef string_map::value_type attribute;
struct const_attribute_iterator
{
typedef attribute value_type;
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
typedef const attribute *pointer;
typedef const attribute &reference;
typedef string_map::const_iterator Itor;
explicit const_attribute_iterator(const Itor &i): i_(i) {}
const_attribute_iterator &operator++() { ++i_; return *this; }
const_attribute_iterator operator++(int) { return const_attribute_iterator(i_++); }
const attribute &operator*() const { return *i_; }
const attribute *operator->() const { return &*i_; }
bool operator==(const const_attribute_iterator &i) const { return i_ == i.i_; }
bool operator!=(const const_attribute_iterator &i) const { return i_ != i.i_; }
private:
Itor i_;
};
class proxy_string
{
t_string &real_str_;
public:
proxy_string(config &owner, const std::string &key)
: real_str_(owner.values[key]) {}
proxy_string &operator=(const proxy_string &other)
{ return this->operator=(other.real_str_); }
proxy_string &operator=(bool);
proxy_string& operator=(const char *str)
{ real_str_ = str; return *this; }
proxy_string& operator=(const std::string &str)
{ real_str_ = str; return *this; }
proxy_string& operator=(const t_string &str)
{ real_str_ = str; return *this; }
proxy_string& operator+=(const char *str)
{ real_str_ += str; return *this; }
proxy_string& operator+=(const std::string &str)
{ real_str_ += str; return *this; }
proxy_string& operator+=(const t_string &str)
{ real_str_ += str; return *this; }
// t_string 'emulation' methods
bool empty() const { return real_str_.empty(); }
const char *c_str() const { return real_str_.c_str(); }
std::string to_serialized() const { return real_str_.to_serialized(); }
const std::string &str() const { return real_str_.str(); }
operator std::string() const { return real_str_.str(); }
operator t_string() const { return real_str_; }
inline friend std::ostream& operator<<(std::ostream &os, const proxy_string &str)
{ return os << str.real_str_; }
};
typedef std::pair<const_attribute_iterator,const_attribute_iterator> const_attr_itors;
typedef std::pair<child_list::iterator, child_list::iterator> child_itors_bak;
child_itors_bak child_range_bak(const std::string &);
child_itors child_range(const std::string& key);
const_child_itors child_range(const std::string& key) const;
size_t child_count(const std::string& key) const;
const child_list& get_children(const std::string& key) const;
/**
* Copies the first child with the given @a key, or an empty config if there is none.
*/
config child_or_empty(const std::string &key) const;
/**
* Returns the nth child with the given @a key, or
* a reference to an invalid config if there is none.
*/
config &child(const std::string& key, int n = 0);
/**
* Returns the nth child with the given @a key, or
* a reference to an invalid config if there is none.
*/
const config &child(const std::string& key, int n = 0) const
{ return const_cast<config *>(this)->child(key, n); }
config& add_child(const std::string& key);
config& add_child(const std::string& key, const config& val);
config& add_child_at(const std::string& key, const config& val, size_t index);
proxy_string operator[](const std::string& key)
{ return proxy_string(*this, key); }
const t_string& operator[](const std::string& key) const;
/**
* Returns a reference to the first child with the given @a key.
* Creates the child if it does not yet exist.
*/
config &child_or_add(const std::string &key);
const t_string& get_attribute(const std::string& key) const;
bool has_attribute(const std::string &key) const;
void remove_attribute(const std::string &key);
void merge_attributes(const config &);
const_attr_itors attribute_range() const;
config &find_child(const std::string& key, const std::string& name,
const t_string& value);
const config &find_child(const std::string& key, const std::string& name,
const t_string& value) const;
const config &find_child_recursive(const std::string& key, const std::string& name,
const t_string& value) const;
void clear_children(const std::string& key);
void remove_child(const std::string& key, size_t index);
void recursive_clear_value(const std::string& key);
void clear();
bool empty() const;
std::string debug() const;
std::string hash() const;
struct error : public game::error {
error(const std::string& message) : game::error(message) {}
};
struct child_pos {
child_pos(child_map::const_iterator p, size_t i) : pos(p), index(i) {}
child_map::const_iterator pos;
size_t index;
bool operator==(const child_pos& o) const { return pos == o.pos && index == o.index; }
bool operator!=(const child_pos& o) const { return !operator==(o); }
};
struct any_child
{
const child_map::key_type &key;
const config &cfg;
any_child(const child_map::key_type *k, const config *c): key(*k), cfg(*c) {}
};
struct all_children_iterator
{
typedef any_child value_type;
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
typedef any_child *pointer;
typedef any_child reference;
typedef std::vector<child_pos>::const_iterator Itor;
explicit all_children_iterator(const Itor &i): i_(i) {}
all_children_iterator &operator++() { ++i_; return *this; }
all_children_iterator operator++(int) { return all_children_iterator(i_++); }
struct arrow_helper
{
any_child data;
arrow_helper(const all_children_iterator &i): data(*i) {}
any_child *operator->() { return &data; }
};
any_child operator*() const;
arrow_helper operator->() const { return *this; }
size_t get_index() const;
bool operator==(const all_children_iterator &i) const { return i_ == i.i_; }
bool operator!=(const all_children_iterator &i) const { return i_ != i.i_; }
private:
Itor i_;
};
typedef std::pair<all_children_iterator, all_children_iterator> all_children_itors;
/** In-order iteration over all children. */
all_children_itors all_children_range() const;
all_children_iterator ordered_begin() const;
all_children_iterator ordered_end() const;
all_children_iterator erase(const all_children_iterator& i);
/**
* A function to get the differences between this object,
* and 'c', as another config object.
* I.e. calling cfg2.apply_diff(cfg1.get_diff(cfg2))
* will make cfg1 identical to cfg2.
*/
config get_diff(const config& c) const;
void get_diff(const config& c, config& res) const;
/**
* The name of the attribute used for tracking diff changes
*/
static const char* diff_track_attribute;
/**
* A function to apply a diff config onto this config object.
*
* If the "track" parameter is true, the changes made will be marked in a
* magic attribute (defined above) of this and child nodes of this config,
* with "new" value indicating an added child, "modified" a modified one,
* and "deleted" for the deleted items, *which will not be actually
* deleted* (so calling code can easily see what they are).
* Use clear_diff_track with the same diff object to clear the tracking
* info and actually delete the nodes.
*/
void apply_diff(const config& diff, bool track = false); //throw error
/**
* Clear any tracking info from a previous apply_diff call with tracking.
* This also removes the nodes that are to be deleted, in effect making
* apply_diff(c, true); clear_diff_tracking(c);
* equivalent to apply_diff(c, false);
*/
void clear_diff_track(const config& diff);
/**
* Merge config 'c' into this config.
* Overwrites this config's values.
*/
void merge_with(const config& c);
bool matches(const config &filter) const;
/**
* Append data from another config object to this one.
* Attributes in the latter config object will clobber attributes in this one.
*/
void append(const config& cfg);
/**
* All children with the given key will be merged
* into the first element with that key.
*/
void merge_children(const std::string& key);
/**
* All children with the given key and with equal values
* of the specified attribute will be merged into the
* element with that key and that value of the attribute
*/
void merge_children_by_attribute(const std::string& key, const std::string& attribute);
//this is a cheap O(1) operation
void swap(config& cfg);
private:
/** All the attributes of this node. */
string_map values;
/** A list of all children of this node. */
child_map children;
std::vector<child_pos> ordered_children;
};
#endif