ultimatepp/bazaar/Form/IniConfig.hpp
koldo ac7821deb8 Form: Form core package, Editor and sample from Sc0rch
git-svn-id: svn://ultimatepp.org/upp/trunk@2412 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2010-05-19 18:38:45 +00:00

583 lines
26 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef INI_CONFIG_HPP
#define INI_CONFIG_HPP
#include <Core/Core.h>
using namespace Upp;
static int IniSaveCharset = CHARSET_DEFAULT;
inline void ReplaceString(String& text, const String& fstr, const String& istr)
{
if (!text.GetCount()) return;
if (!fstr.GetCount()) return;
int pos = 0;
while (pos >= 0)
{
pos = text.Find(fstr, pos);
if (pos >= 0)
{
text.Remove(pos, fstr.GetCount());
if (!istr.IsEmpty())
text.Insert(pos, istr);
pos += istr.GetCount();
}
}
}
//! Простой фильтр символов для строк.
//! \brief Простой фильтр символов для строк.
//! Пример:
//! 1) "- +/" - удалит из строки все пробелы и удвоит слэш;
//! 2) "-+" удалит все знаки + из строки
//! 3) "--" удалит все знаки - из строки
//! 4) "---+" удалит все знаки + и - из строки
//! \param text текст для обработки
//! \param filter строка символов фильтра
inline String SetFilter(const String& text, const String& filter)
{
Vector<char> f[2];
String result;
bool flag;
bool del = false;
for (int cur = -1, i = 0; i < filter.GetCount(); ++i)
switch(filter[i])
{
case '+': if (del) { f[1] << filter[i]; del = false; } cur = 1; break;
case '-': if (del) { f[1] << filter[i]; del = false; }
else del = true; cur = 2; break;
default:
if (cur == -1)
continue;
cur == 1
? f[0] << filter[i]
: f[1] << filter[i];
}
for (int i = 0; i < text.GetCount(); ++i)
{
for (int j = 0; j < f[1].GetCount(); ++j)
{
flag = (char)text[i] == f[1][j];
if (flag) break;
}
if (!flag)
for (int j = 0; j < f[0].GetCount(); ++j)
{
if ((char)text[i] == f[0][j])
{
result << (char)text[i];
flag = false;
}
}
if (!flag) result << (char)text[i];
}
return result;
}
//! Младший элемент INI-дерева, хранит текстовое поле, и предоставляет основные ф-и
//! для работы с ним
struct IniText
{
//! Основной конструктор
IniText() {}
//! Расширенный конструктор
//! \param text текст для элемента
//! \param str флаг строки
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и ф-я работает быстрее
IniText(const String& text, bool str, bool manual = true) { SetText(text, str, manual); }
//! Устанавливает значение текстового поля элемента
//! \param text текст для элемента
//! \param str флаг строки
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и ф-я работает быстрее
void SetText(String text, bool str, bool manual = true)
{
if (text.Find('\\') >= 0) ::ReplaceString(text, "\\", "\\\\");
if (text.Find('\"') >= 0) ::ReplaceString(text, "\"", "\\\"");
if (text.Find('[' ) >= 0) ::ReplaceString(text, "[", "\\[");
if (text.Find(']' ) >= 0) ::ReplaceString(text, "]", "\\]");
if (text.Find('=' ) >= 0) ::ReplaceString(text, "=", "\\=");
if (text.Find(',' ) >= 0) ::ReplaceString(text, ",", "\\,");
Text = Format(str ? "\"%s\"" : "%s",
manual ? SetFilter(text, str ? "-\r\n\t" : "- \r\n\t") : text);
}
//! Сохраняет элемент в поток с форматированием
//! \param s поток данных
//! \param f формат вывода в поток (%s - значение элемента)
void Serialize(Stream& s, const String& f = "%s")
{
s << ::ToCharset(IniSaveCharset, Format(f, Text), CHARSET_DEFAULT);
}
String GetText()
{
if (Text.GetCount() == 1 && Text[0] == '\"')
return "";
else if (Text.GetCount())
{
String t = Text;
if (Text[0] == '\"' && Text[Text.GetCount() - 1] == '\"')
t = Text.Mid(1, Text.GetCount()-2);
if (t.Find("\\\"") >= 0) ::ReplaceString(t, "\\\"", "\"");
if (t.Find("\\[" ) >= 0) ::ReplaceString(t, "\\[", "[");
if (t.Find("\\]" ) >= 0) ::ReplaceString(t, "\\]", "]");
if (t.Find("\\=" ) >= 0) ::ReplaceString(t, "\\=", "=");
if (t.Find("\\," ) >= 0) ::ReplaceString(t, "\\,", ",");
if (t.Find("\\\\") >= 0) ::ReplaceString(t, "\\\\", "\\");
return t;
}
return Text;
}
//! Текстовое поле элемента, значение поля
String Text;
};
//! Элемент INI-дерева, значение в структуре INI-файла
//! \brief Элемент INI-дерева, значение в структуре INI-файла.
//! Например, в строке "[Section] Name = Value" значением является Value.
struct IniValue : IniText, Moveable<IniValue>
{
//! Основной конструктор
IniValue() {}
//! Конструктор из другого IniValue-значения
IniValue(const IniValue& other) { Text = other.Text; }
//! Расширенный конструктор
//! \param text текст для элемента
//! \param str флаг строки
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и ф-я работает быстрее
IniValue(const String& text, bool str, bool manual = true) : IniText(text, str, manual) {}
};
//! Элемент INI-дерева, ключ в структуре INI-файла, который содержит одно/несколько значений
//! \brief Элемент INI-дерева, ключ в структуре INI-файла, который содержит одно/несколько
//! значений. Например, в строке "[Section] Name = Value" ключом является Name.
struct IniName : IniText, Moveable<IniName>
{
//! Основной конструктор
IniName() {}
//! Конструктор из другого IniName-значения
IniName(const IniName& other) { Text = other.Text; Content <<= other.Content; }
//! Расширенный конструктор
//! \param text текст для элемента
//! \param str флаг строки
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и ф-я работает быстрее
IniName(const String& text, bool str, bool manual = true) : IniText(text, str, manual) {}
//! Устанавливает значение ключа, предыдущие стирает
//! \param v значение ключа
void Set(const IniValue& v) { Content.Clear(); Content << v; }
//! Добавляет значение к имеющимся в ключе
//! \param v значение ключа
void Add(const IniValue& v) { Content << v; }
//! Массив значений, которым назначен ключ
Vector<IniValue> Content;
};
//! Элемент INI-дерева, является секцией в структуре INI-файла, содержит ключи и значения
//! \brief Элемент INI-дерева, является секцией в структуре INI-файла, содержит ключи и
//! значения. Например, в строке "[Section] Name = Value" секцией является Section.
struct IniSection : IniText, Moveable<IniSection>
{
//! Основной конструктор
IniSection() {}
//! Конструктор из другого IniSection-значения
IniSection(const IniSection& other) { Text = other.Text; Content <<= other.Content; }
//! Расширенный конструктор
//! \param text текст для элемента
//! \param str флаг строки
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и ф-я работает быстрее
IniSection(const String& text, bool str, bool manual = true) : IniText(text, str, manual) {}
//! Сохраняет секцию в поток с форматированием каждого элемента
//! \param s поток данных
void Serialize(Stream& s)
{
if (s.IsStoring())
{
IniText::Serialize(s, "[%s]\r\n");
for (int i = 0; i < Content.GetCount(); ++i)
{
Content[i].Serialize(s, "%s = ");
for (int j = 0; j < Content[i].Content.GetCount(); ++j)
{
Content[i].Content[j].
Serialize(s, j == Content[i].Content.GetCount() - 1 ? "%s\r\n" : "%s, ");
}
}
}
}
//! Возвращает индекс ключа в массиве. Если ключ не найден, возвращает -1
//! \param name название ключа для поиска
int FindName(const String& name)
{
for (int i = 0; i < Content.GetCount(); ++i)
if (Content[i].Text == name) return i;
return -1;
}
//! Устанавливает значение ключа (если ключ не существует, он будет создан),
//! предыдущие стирает
//! \param n ключ для изменения
//! \param v значение ключа
void Set(const IniName& n, const IniValue& v)
{
if (n.Text.IsEmpty()) return;
int i = FindName(n.Text);
if (i < 0) { i = Content.GetCount(); Content << n; }
if (v.Text.IsEmpty()) return;
Content[i].Set(v);
}
//! Добавляет значение к имеющимся в ключе
//! \param v значение ключа
//! Добавляет значение к имеющимся в ключе (если ключ не существует, он будет создан)
//! \param n ключ для изменения
//! \param v значение ключа
void Add(const IniName& n, const IniValue& v)
{
if (n.Text.IsEmpty()) return;
int i = FindName(n.Text);
if (i < 0) { i = Content.GetCount(); Content << n; }
if (v.Text.IsEmpty()) return;
Content[i].Add(v);
}
//! Массив ключей со значениям
Vector<IniName> Content;
};
//! Конверт для строки, из которого может быть создан INI-элемент любого типа
class IniConvert : IniText
{
public:
//! Контруктор из строкового значения
//! \param text строка для преобразования
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и преобразование конверта работает быстрее
IniConvert(const String& text, bool manual) { Text = text; Manual = manual; }
//! Контруктор из целого числа
//! \param int целое число для преобразования
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и преобразование конверта работает быстрее
IniConvert(int num, bool manual) { Text = AsString(num); Manual = manual; }
//! Контруктор из логического значения
//! \param int логическое значения для преобразования
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и преобразование конверта работает быстрее
IniConvert(bool flag, bool manual) { Text = flag ? "1" : "0"; Manual = manual; }
//! Контруктор из числа с плавающей точкой
//! \param int число с плавающей точкой для преобразования
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и преобразование конверта работает быстрее
IniConvert(double num, bool manual) { Text = FormatDouble(num); Manual = manual; }
//! Оператор преобразования значения в IniName-элемент
operator IniName() const { return IniName(Text, Text.Find(' ') >= 0, Manual); }
//! Оператор преобразования значения в IniValue-элемент
operator IniValue() const { return IniValue(Text, Text.Find(' ') >= 0, Manual); }
//! Оператор преобразования значения в IniSection-элемент
operator IniSection() const { return IniSection(Text, Text.Find(' ') >= 0, Manual); }
private:
//! Флаг оптимизации, при ложном значении отключена фильтрация значения и преобразование
//! конверта работает быстрее
bool Manual;
};
//! Простой класс для работы с INI-файлами
//! \brief Простой класс для работы с INI-файлами. Если строка не заключена в кавычки \",
//! некоторые символы будут пропущены (например, пробелы). Можно записывать несколько значений
//! после имени ключа (через запятую).
//! Пример INI-файла:
//! \code
//! [General]
//! Title = "Application Title"
//! Language = Ru-Ru
//!
//! ["Section 1"] // Section 1
//! Item 0 = 0,0, Name 0, Path0 // Item0 = 0,0,Name0,Path0 - без кавычек
//! "Item 1" = 0,1, "Name 1", "Path 1" // Item 1 = 0,1,Name 1,Path 1 - при использовании кавычек
//! \endcode
//! Пример работы с классом:
//! \code
//! IniFile p;
//! p.Load("C:\\Test.ini");
//! p.Add("Test", "String", "Value");
//! p.Add("Test", "Double", 100.01);
//! p.Add("Test", "Number", 1);
//! p.Add("Test", "Boolean", true); // true хранится в файле как 1, false - как 0
//! p.Save("C:\\Test.ini");
//! \endcode
class IniFile
{
public:
//! Устанавливает строковое значение ключа в секции (если секция или ключ не существуют,
//! они будут созданы), предыдущие стирает
//! \param s секция для поиска ключа
//! \param n ключ в секции для изменения
//! \param v строковое значение ключа
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и ф-я работает быстрее
void Set(const String& s, const String& n, const String& v, bool manual = true)
{ Set(IniConvert(s, manual), IniConvert(n, manual), IniConvert(v, manual)); }
//! Записывает массив значений в ключ секции (если секция или ключ не существуют,
//! они будут созданы), предыдущие значения стирает
//! \param s секция для поиска ключа
//! \param n ключ в секции для изменения
//! \param a массив значений для записи
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и ф-я работает быстрее
void SetArray(const String& s, const String& n, const Array<String>& a, bool manual = true)
{
for (int i = 0; i < a.GetCount(); ++i)
Set(IniConvert(s, manual), IniConvert(n, manual), IniConvert(a[i], manual));
}
//! Устанавливает целочисленное значение ключа в секции (если секция или ключ не существуют,
//! они будут созданы), предыдущие стирает
//! \param s секция для поиска ключа
//! \param n ключ в секции для изменения
//! \param v целочисленное значение ключа
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и ф-я работает быстрее
void SetInt(const String& s, const String& n, int v, bool manual = true)
{ Set(IniConvert(s, manual), IniConvert(n, manual), IniConvert(v, manual)); }
//! Устанавливает число с плавающей точкой как значение ключа в секции (если секция или
//! ключ не существуют, они будут созданы), предыдущие значения стирает
//! \param s секция для поиска ключа
//! \param n ключ в секции для изменения
//! \param v число с плавающей точкой, значение ключа
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и ф-я работает быстрее
void SetDouble(const String& s, const String& n, double v, bool manual = true)
{ Set(IniConvert(s, manual), IniConvert(n, manual), IniConvert(v, manual)); }
//! Устанавливает значение ключа в секции (если секция или ключ не существуют,
//! они будут созданы), предыдущие стирает
//! \param s секция для поиска ключа
//! \param n ключ в секции для изменения
//! \param v значение ключа
void Set(const IniSection& s, const IniName& n, const IniValue& v)
{
if (s.Text.IsEmpty()) return;
int i = FindSection(s.Text);
if (i < 0) { i = Content.GetCount(); Content << s; }
if (n.Text.IsEmpty()) return;
Content[i].Set(n, v);
}
//! Добавляет строковое значение к имеющимся в ключе секции (если секция или ключ
//! не существуют, они будут созданы)
//! \param s секция для поиска
//! \param n ключ для изменения
//! \param v строковое значение ключа
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и ф-я работает быстрее
void Add(const String& s, const String& n, const String& v, bool manual = true)
{ Add(IniConvert(s, manual), IniConvert(n, manual), IniConvert(v, manual)); }
//! Дописывает массив значений в ключ секции (если секция или ключ не существуют,
//! они будут созданы)
//! \param s секция для поиска ключа
//! \param n ключ в секции для изменения
//! \param a массив значений для записи
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и ф-я работает быстрее
void AddArray(const String& s, const String& n, const Array<String>& a, bool manual = true)
{
for (int i = 0; i < a.GetCount(); ++i)
Add(IniConvert(s, manual), IniConvert(n, manual), IniConvert(a[i], manual));
}
//! Добавляет целочисленное значение к имеющимся в ключе секции (если секция или ключ
//! не существуют, они будут созданы)
//! \param s секция для поиска
//! \param n ключ для изменения
//! \param v целочисленное значение ключа
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и ф-я работает быстрее
void AddInt(const String& s, const String& n, int v, bool manual = true)
{ Add(IniConvert(s, manual), IniConvert(n, manual), IniConvert(v, manual)); }
//! Добавляет число с плавающей точкой как значение к имеющимся в ключе секции
//! (если секция или ключ не существуют, они будут созданы)
//! \param s секция для поиска
//! \param n ключ для изменения
//! \param v логическое значение ключа
//! \param manual флаг оптимизации, при ложном значении отключена фильтрация
//! и ф-я работает быстрее
void AddDouble(const String& s, const String& n, double v, bool manual = true)
{ Add(IniConvert(s, manual), IniConvert(n, manual), IniConvert(v, manual)); }
//! Добавляет значение к имеющимся в ключе секции (если секция или ключ не существуют,
//! они будут созданы)
//! \param s секция для поиска ключа
//! \param n ключ для изменения
//! \param v число с плавающей точкой, значение ключа
void Add(const IniSection& s, const IniName& n, const IniValue& v)
{
if (s.Text.IsEmpty()) return;
int i = FindSection(s.Text);
if (i < 0) { i = Content.GetCount(); Content << s; }
if (n.Text.IsEmpty()) return;
Content[i].Add(n, v);
}
//! Возвращает строковое значение ключа в секции
//! \param s секция для поиска ключа
//! \param n ключ для поиска значения
String Get(const String& s, const String& n)
{
Vector<String> values = GetArray(s, n);
return values.GetCount() ? values[0] : "";
}
//! Возвращает массив значений ключа в секции
//! \param s секция для поиска ключа
//! \param n ключ для поиска значения
Vector<String> GetArray(const String& s, const String& n)
{
int i = FindSection(s);
if (i < 0) return Vector<String>();
int j = Content[i].FindName(n);
if (j < 0) return Vector<String>();
Vector<String> result;
for (int v = 0; v < Content[i].Content[j].Content.GetCount(); ++v)
result << Content[i].Content[j].Content[v].GetText();
return result;
}
//! Возвращает целочисленное значение ключа в секции
//! \param s секция для поиска ключа
//! \param n ключ для поиска значения
int GetInt(const String& s, const String& n)
{
Vector<String> values = GetArray(s, n);
return values.GetCount() ? ScanInt(values[0]) : 0;
}
//! Возвращает значение ключа секции в виде числа с плавающей точкой
//! \param s секция для поиска ключа
//! \param n ключ для поиска значения
double GetDouble(const String& s, const String& n)
{
Vector<String> values = GetArray(s, n);
return values.GetCount() ? ScanDouble(values[0]) : 0.0;
}
//! Возвращает массив с перечислением всех секций файла
Vector<String> EnumSections();
//! Возвращает массив с перечислением всех ключей определенной секции файла
//! \param s секция для поиска ключей
Vector<String> EnumNames(const String& s);
//! Загружает INI-структуру из файла. При возникновении ошибки возвращает false
//! \param file файл для загрузки
virtual bool Load(const String& file, int charset = CHARSET_DEFAULT);
//! Загружает INI-структуру из потока. При возникновении ошибки возвращает false
//! \param s поток для загрузки
virtual bool Load(Stream& s, int charset = CHARSET_DEFAULT);
//! Сохраняет INI-структуру в файл. При возникновении ошибки возвращает false
//! \param file файл для сохранения
virtual bool Save(const String& file, int charset = CHARSET_DEFAULT)
{
IniSaveCharset = charset;
FileOut out;
if (!out.Open(file))
return false;
bool r = Save(out);
out.Close();
return r;
}
//! Сохраняет INI-структуру в поток. При возникновении ошибки возвращает false
//! \param s поток для сохранения
virtual bool Save(Stream& s)
{
if (!s.IsOpen())
return false;
Serialize(s);
return true;
}
//! Возвращает INI-структуру в виде строки
String Dump()
{
StringStream out; out.Create();
Serialize(out);
out.Close();
return out;
}
//! Удаляет все элементы в INI-структуре
void Clear() { Content.Clear(); }
protected:
//! Сохраняет файл в поток
//! \param s поток данных
void Serialize(Stream& s)
{
if (s.IsStoring())
for (int i = 0; i < Content.GetCount(); ++i)
{
Content[i].Serialize(s);
if (i < Content.GetCount() - 1) s << "\r\n";
}
}
//! Возвращает индекс секции в массиве. Если секция не найдена, возвращает -1
//! \param section название секции для поиска
int FindSection(const String& section)
{
for (int i = 0; i < Content.GetCount(); ++i)
if (Content[i].Text == section) return i;
return -1;
}
//! Массив секций с ключами и значениями, INI-структура
Vector<IniSection> Content;
};
#endif // .. INI_CONFIG_HPP