/******************************************************************************/
/*! @file Keys.cc
    @brief Keys class
    @author Masashi Astro Tachibana, Apolloron Project.
 ******************************************************************************/

#include <stdio.h>
#include <string.h>
#include "apolloron.h"

namespace apolloron {


// change 1 byte value to 6bit vale (0-63)
#define b64(a) (((unsigned char)(a) & 0xfc) >> 2)

/*! Constructor of Keys.
    @param void
    @return void
 */
Keys::Keys() {
    int i;

    (*this).pElement = new Element ** [64];
    for (i = 0; i < 64; i++) {
        (*this).nElementTotal[i] = 0;
        (*this).nElementStart[i] = -1;
        (*this).pElement[i] = (Element **)NULL;
    }

    (*this).tmpString = (String *)NULL;
    (*this).emptyString = new String;
    *((*this).emptyString) = ""; // stay empty
}


/*! Constructor of Keys.
    @param keys: Keys for initialization.
    @return void
 */
Keys::Keys(const Keys &keys) {
    int i;

    (*this).pElement = new Element ** [64];
    for (i = 0; i < 64; i++) {
        (*this).nElementTotal[i] = 0;
        (*this).nElementStart[i] = -1;
        (*this).pElement[i] = (Element **)NULL;
    }

    (*this).tmpString = (String *)NULL;
    (*this).emptyString = new String;
    *((*this).emptyString) = ""; // stay empty

    (*this).set(keys);
}


/*! Destructor of Keys.
    @param void
    @return void
 */
Keys::~Keys() {
    int i;
    long l;

    if ((*this).pElement != NULL && *((*this).pElement) != NULL) {
        for (i = 0; i < 64; i++) {
            l = 0;
            while (l < (*this).nElementTotal[i]) {
                if ((*this).pElement[i][l] != NULL) {
                    if ((*this).pElement[i][l]->key != NULL) {
                        delete [] (*this).pElement[i][l]->key;
                    }
                    delete (*this).pElement[i][l]->value;
                    delete [] (*this).pElement[i][l];
                }
                l++;
            }
            delete *((*this).pElement[i]);
            (*this).nElementTotal[i] = 0;
        }
        delete [] *((*this).pElement);
        (*this).pElement = NULL;
    }

    if ((*this).tmpString != (String *)NULL) {
        delete (*this).tmpString;
        (*this).tmpString = (String *)NULL;
    }

    if ((*this).emptyString != (String *)NULL) {
        delete (*this).emptyString;
        (*this).emptyString = (String *)NULL;
    }
}

/*! Delete instance of Keys.
    @param void
    @retval true   success
    @retval false  failure
 */
bool Keys::clear() {
    int i;
    long l;

    if ((*this).pElement != NULL && *((*this).pElement) != NULL) {
        for (i = 0; i < 64; i++) {
            l = 0;
            while (l < (*this).nElementTotal[i]) {
                if ((*this).pElement[i][l] != NULL) {
                    if ((*this).pElement[i][l]->key != NULL) {
                        delete [] (*this).pElement[i][l]->key;
                    }
                    delete (*this).pElement[i][l]->value;
                    delete [] (*this).pElement[i][l];
                }
                l++;
            }
            delete *((*this).pElement[i]);
            (*this).nElementTotal[i] = 0;
        }
        delete [] *((*this).pElement);
        (*this).pElement = NULL;
    }

    (*this).pElement = new Element ** [64];
    for (i = 0; i < 64; i++) {
        (*this).nElementTotal[i] = 0;
        (*this).nElementStart[i] = -1;
        (*this).pElement[i] = (Element **)NULL;
    }

    if ((*this).tmpString != (String *)NULL) {
        delete (*this).tmpString;
        (*this).tmpString = (String *)NULL;
    }

    return true;
}


/*! Get an element that have match key.
    @param key  key of object
    @return String object
 */
String& Keys::operator [] (const String &key) {
    if ((*this).isKeyExist(key) == false) {
        (*this).addKey(key, "");
    }
    return (*this).getValue(key.c_str());
}


/*! Get an element that have match key.
    @param key  key of object
    @return String object
 */
const String& Keys::operator [] (const String &key) const {
    return (const String&)(*this).getValue(key.c_str());
}


/*! Get an element that have match key.
    @param key  key of object
    @return String object
 */
String& Keys::operator [] (const char *key) {
    if ((*this).isKeyExist(key) == false) {
        (*this).addKey(key, "");
    }
    return (*this).getValue(key);
}


/*! Get an element that have match key.
    @param key  key of object
    @return String object
 */
const String& Keys::operator [] (const char *key) const {
    return (const String&)(*this).getValue(key);
}


/*! Get value of an element that have match key.
    @param key  key of object
    @return Keys object
 */
String& Keys::getValue(const String &key) const {
    return (*this).getValue(key.c_str());
}


/*! Get value of an element that have match key.
    @param key  key of object
    @return Keys object
 */
String& Keys::getValue(const char *key) const {
    Element *element;
    bool found;
    int start;

    element = NULL;

    if (key == NULL) {
        key = "";
    }

    found = false;
    start = b64(key[0]);
    if (0 < (*this).nElementTotal[start]) {
        element = (*this).pElement[start][(*this).nElementStart[start]];
        while (element != NULL && element->key != NULL) {
            int cmp;
            cmp = strcmp(key, element->key);
            if (cmp == 0) {
                // found!
                found = true;
                break;
            } else if (cmp < 0) {
                // small
                if (element->small == -1) break;
                element = (*this).pElement[start][element->small];
            } else {
                // big
                if (element->big == -1) break;
                element = (*this).pElement[start][element->big];
            }
        }
    }

    if (found == false) {
        if ((*((*this).emptyString)).c_str()[0] != '\0') {
            *((*this).emptyString) = "";
        }
        return *((*this).emptyString);
    }
    return *((*element).value);
}


/*! Get value of an element that have match key. (for multi-threaded use)
    @param key  key of object
    @return Keys object
 */
const char* Keys::read(const String &key) const {
    return (*this).read(key.c_str());
}


/*! Get value of an element that have match key. (for multi-threaded use)
    @param key  key of object
    @return Keys object
 */
const char* Keys::read(const char *key) const {
    Element *element;
    bool found;
    int start;

    element = NULL;

    found = false;
    start = b64(key[0]);

    if (0 < (*this).nElementTotal[start]) {
        element = (*this).pElement[start][(*this).nElementStart[start]];
        while (element != NULL && element->key != NULL) {
            int cmp;
            cmp = strcmp(key, element->key);
            if (cmp == 0) {
                // found!
                found = true;
                break;
            } else if (cmp < 0) {
                // small
                if (element->small == -1) break;
                element = (*this).pElement[start][element->small];
            } else {
                // big
                if (element->big == -1) break;
                element = (*this).pElement[start][element->big];
            }
        }
    }

    if (found == false) {
        return NULL;
    }

    return (*((*element).value)).c_str();
}


/*! Check the existance of key.
    @param key  key of object
    @retval true  key exist
    @retval false key not exist
 */
bool Keys::isKeyExist(const String &key) const {
    return isKeyExist(key.c_str());
}


/*! Check the existance of key.
    @param key  key of object
    @retval true  key exist
    @retval false key not exist
 */
bool Keys::isKeyExist(const char *key) const {
    bool found;
    Element *element;

    found = false;

    if (key != NULL) {
        int start;
        start = b64(key[0]);
        if (0 < (*this).nElementTotal[start]) {
            element = (*this).pElement[start][(*this).nElementStart[start]];
            while (element != NULL && element->key != NULL) {
                int cmp;
                cmp = strcmp(key, element->key);
                if (cmp == 0) {
                    // found!
                    found = true;
                    break;
                } else if (cmp < 0) {
                    // small
                    if (element->small == -1) break;
                    element = (*this).pElement[start][element->small];
                } else {
                    // big
                    if (element->big == -1) break;
                    element = (*this).pElement[start][element->big];
                }
            }
        }
    }

    return found;
}


/*! get the Number of keys in array.
    @param void
    @return number of keys
 */
long Keys::max() const {
    int i;
    long total;

    total = 0;

    for (i = 0; i < 64; i++) {
        total += (*this).nElementTotal[i];
    }

    return total;
}


/*! Get key of array.
    @param index  index of key
    @return key string
 */
const char *Keys::key(long index) const {
    Element *element;
    element = array(index);
    return element->key;
}


/*! Setup of keys.
    @param keys  Keys object to set
    @retval true  success
    @retval false failure
 */
bool Keys::set(const Keys & keys) {
    long i;

    clear();

    i = 0;
    while (i < keys.max()) {
        addKey(keys.array(i)->key, *(keys.array(i)->value));
        i++;
    }

    return true;
}


/*! Setup of keys.
    @param keys  Keys object to set
    @return Keys object
 */
const Keys& Keys::operator = (const Keys &keys) {
    set(keys);

    return *this;
}


/*! Get value of key in array.
    @param keys  Keys object to set
    @return Keys object
 */
String& Keys::value(long index) const {
    Element *element;
    element = array(index);
    return *(element->value);
}


/*! Get element of array
    @param index index of Keys
    @return Element object
 */
Element* Keys::array(long index) const {
    Element *element;

    element = (Element *)NULL;

    if (0 <= index) {
        int i;
        int start;
        long total;

        start = -1;
        total = 0;
        for (i = 0; i < 64; i++) {
            total += (*this).nElementTotal[i];
            if (index < total) {
                start = i;
                break;
            }
        }

        if (0 <= start) {
            long internal_index = index - (total - (*this).nElementTotal[start]);
            if (0 <= internal_index && internal_index <= (*this).nElementTotal[start]) {
                element = (*this).pElement[start][internal_index];
            }
        }
        /*
            if (0 <= start) {
              long internal_index;
              long *stack;
              char *stack_info; // -1: small, 1: big
              long stacknum;

              internal_index = index - (total - (*this).nElementTotal[start]);
              stack = new long[(*this).nElementTotal[start] + 1];
              stack_info = new char[(*this).nElementTotal[start] + 1];
              stacknum = 0;
              stack[stacknum] = (*this).nElementStart[start];
              stack_info[stacknum] = -1;
              stacknum++;
              stack[stacknum] = -1;

              if (0 <= internal_index && internal_index <= (*this).nElementTotal[start]) {
                long count = 0;
                while (count < internal_index) {
                  if (stack_info[stacknum - 1] == -1) {
                    long small = (*this).pElement[start][stack[stacknum - 1]]->small;
                    stack_info[stacknum - 1] = 1;
                    if (0 <= small) {
                      stack[stacknum] = small;
                      stack_info[stacknum] = -1;
                      stacknum++;
                      stack[stacknum] = -1;
                      count++;
                    }
                  } else if (stack_info[stacknum - 1] == 1) {
                    long big = (*this).pElement[start][stack[stacknum - 1]]->big;
                    stack_info[stacknum - 1] = 0;
                    if (0 <= big) {
                      stack[stacknum] = big;
                      stack_info[stacknum] = -1;
                      stacknum++;
                      stack[stacknum] = -1;
                      count++;
                    }
                  } else {
                    stack[stacknum] = -1;
                    if (stacknum <= 1) {
                      break;
                    }
                    stacknum--;
                  }
                }
              }

              element = (*this).pElement[start][stack[stacknum - 1]];
              delete [] stack_info;
              delete [] stack;
            }
        */
    }

    return element;
}


/*! Append a key to array
    @param key   key of array
    @param value value to append
    @retval true  success
    @retval false failure
 */
bool Keys::addKey(const char *key, const String &value) {
    return addKey(key, value.c_str());
}


/*! Append a key to array
    @param key   key of array
    @param value value to append
    @retval true  success
    @retval false failure
 */
bool Keys::addKey(const char *key, const char *value) {
    if (key != NULL) {
        int start;

        start = b64(key[0]);
        if ((*this).nElementTotal[start] == 0) {
            (*this).nElementStart[start] = 0;
            (*this).pElement[start] = new (Element *);
            (*this).pElement[start][0] = new Element;
            (*this).pElement[start][0]->key = new char[strlen(key) + 1];
            strcpy((*this).pElement[start][0]->key, key);
            (*this).pElement[start][0]->value = new String;
            (String &)(*((*this).pElement[start][0]->value)) = value;
            (*this).pElement[start][0]->small = -1;
            (*this).pElement[start][0]->big = -1;
            (*this).nElementTotal[start] = 1;
        } else {
            Element *parent_element;
            Element *target_element;
            long parent;
            long target;

            parent = (*this).nElementStart[start];
            parent_element = (*this).pElement[start][parent];
            target = -1;
            while (parent_element != NULL && parent_element->key != NULL) {
                int cmp;
                cmp = strcmp(key, parent_element->key);
                if (cmp == 0) {
                    // ** replace (same key found) **
                    if (value != NULL) {
                        delete parent_element->value;
                        parent_element->value = new String;
                        (String &)(*(parent_element->value)) = value;
                    }
                    break;
                } else {
                    Element **element_p;
                    if (cmp < 0) {
                        // small
                        target = parent_element->small;
                    } else {
                        // big
                        target = parent_element->big;
                    }
                    if (target < 0) {
                        // ** add **
                        element_p = new Element * [(*this).nElementTotal[start] + 1];
                        memcpy(element_p, (*this).pElement[start], sizeof(Element *) * (*this).nElementTotal[start]);
                        delete (*this).pElement[start];
                        (*this).pElement[start] = element_p;
                        (*this).pElement[start][(*this).nElementTotal[start]] = new Element;
                        (*this).pElement[start][(*this).nElementTotal[start]]->key = new char[strlen(key) + 1];
                        strcpy((*this).pElement[start][(*this).nElementTotal[start]]->key, key);
                        (*this).pElement[start][(*this).nElementTotal[start]]->value = new String;
                        *((*this).pElement[start][(*this).nElementTotal[start]]->value) = value;
                        (*this).pElement[start][(*this).nElementTotal[start]]->small = -1;
                        (*this).pElement[start][(*this).nElementTotal[start]]->big = -1;
                        if (cmp < 0) {
                            parent_element->small = (*this).nElementTotal[start];
                        } else {
                            parent_element->big = (*this).nElementTotal[start];
                        }
                        (*this).nElementTotal[start]++;
                        break;
                    } else {
                        char *small_key, *big_key;
                        target_element = (*this).pElement[start][target];
                        small_key = (0 <= target_element->small)?(*this).pElement[start][target_element->small]->key:NULL;
                        big_key = (0 <= target_element->big)?(*this).pElement[start][target_element->big]->key:NULL;
                        if (small_key != NULL && 0 < strcmp(small_key, key) && big_key != NULL && strcmp(key, big_key) < 0) {
                            // insert
                            element_p = new Element * [(*this).nElementTotal[start] + 1];
                            memcpy(element_p, (*this).pElement[start], sizeof(Element *) * (*this).nElementTotal[start]);
                            delete (*this).pElement[start];
                            (*this).pElement[start] = element_p;
                            (*this).pElement[start][(*this).nElementTotal[start]] = new Element;
                            (*this).pElement[start][(*this).nElementTotal[start]]->key = new char[strlen(key) + 1];
                            strcpy((*this).pElement[start][(*this).nElementTotal[start]]->key, key);
                            (*this).pElement[start][(*this).nElementTotal[start]]->value = new String;
                            (String &)(*((*this).pElement[start][(*this).nElementTotal[start]]->value)) = value;
                            (*this).pElement[start][(*this).nElementTotal[start]]->small = -1;
                            (*this).pElement[start][(*this).nElementTotal[start]]->big = -1;
                            // process for cycling tree structure
                            if (0 < strcmp(small_key, key) && strcmp(key, target_element->key) < 0) {
                                if (cmp < 0) {
                                    (*this).pElement[start][(*this).nElementTotal[start]]->big = parent_element->small;
                                    (*this).pElement[start][(*this).nElementTotal[start]]->small = target_element->small;
                                    parent_element->small = (*this).nElementTotal[start];
                                    target_element->small = -1;
                                } else {
                                    (*this).pElement[start][(*this).nElementTotal[start]]->big = parent_element->small;
                                    (*this).pElement[start][(*this).nElementTotal[start]]->big = target_element->big;
                                    parent_element->small = (*this).nElementTotal[start];
                                    target_element->big = -1;
                                }
                            } else {
                                if (cmp < 0) {
                                    (*this).pElement[start][(*this).nElementTotal[start]]->small = parent_element->big;
                                    (*this).pElement[start][(*this).nElementTotal[start]]->small = target_element->small;
                                    parent_element->big = (*this).nElementTotal[start];
                                    target_element->small = -1;
                                } else {
                                    (*this).pElement[start][(*this).nElementTotal[start]]->small = parent_element->big;
                                    (*this).pElement[start][(*this).nElementTotal[start]]->big = target_element->big;
                                    parent_element->big = (*this).nElementTotal[start];
                                    target_element->big = -1;
                                }
                            }
                            (*this).nElementTotal[start]++;
                            break;
                        }
                    }
                }
                parent = target;
                parent_element = (*this).pElement[start][parent];
            }
        }
    }

    return true;
}


/*! remove a key from array
    @param key   key of array
    @retval true  success
    @retval false failure
 */
bool Keys::delKey(const String &key) {
    return (*this).delKey(key.c_str());
}


/*! remove a key from array
    @param key   key of array
    @retval true  success
    @retval false failure
 */
bool Keys::delKey(const char *key) {
    if (key != NULL) {
        int start;

        start = b64(key[0]);
        if (0 < (*this).nElementTotal[start]) {
            Element *parent_element;
            Element *target_element;
            Element *child_element;
            long parent;
            long target;
            long child;

            parent = -1;
            parent_element = (Element *)NULL;
            target = (*this).nElementStart[start];
            target_element = (*this).pElement[start][target];
            while (target_element != NULL && target_element->key != NULL) {
                int cmp;
                cmp = strcmp(key, target_element->key);
                if (cmp == 0) {
                    // ** remove (found) **
                    if (0 <= target_element->small && 0 <= target_element->big) {
                        long *child_p;
                        if (parent < 0 || parent_element->small == target) {
                            // biggest key of smaller node.
                            child_p = &(target_element->small);
                            child = target_element->small;
                            child_element = (*this).pElement[start][child];
                            while (0 <= child_element->big) {
                                child_p = &(child_element->big);
                                child = child_element->big;
                                child_element = (*this).pElement[start][child];
                            }
                            *child_p = -1;
                            child_element->big = target;
                            if (parent < 0) {
                                (*this).nElementStart[start] = child;
                            } else {
                                parent_element->small = child;
                            }
                        } else {
                            // smallest key of bigger node
                            child_p = &(target_element->big);
                            child = target_element->big;
                            child_element = (*this).pElement[start][child];
                            while (0 <= child_element->small) {
                                child_p = &(child_element->small);
                                child = child_element->small;
                                child_element = (*this).pElement[start][child];
                            }
                            *child_p = -1;
                            child_element->small = target;
                            parent_element->big = child;
                        }
                    } else if (0 <= parent) {
                        if (target_element->small < 0 && target_element->big < 0) {
                            if (parent_element->small == target) {
                                parent_element->small = -1;
                            }
                            if (parent_element->big == target) {
                                parent_element->big = -1;
                            }
                        } else {
                            child = (0 <= target_element->small)?target_element->small:target_element->big;
                            if (parent_element->small == target) {
                                parent_element->small = child;
                            } else {
                                parent_element->big = child;
                            }
                        }
                    } else {
                        child = (0 <= target_element->small)?target_element->small:target_element->big;
                        (*this).nElementStart[start] = child;
                    }
                    if (target_element->key != NULL) {
                        delete [] target_element->key;
                    }
                    delete target_element->value;
                    delete target_element;
                    (*this).nElementTotal[start]--;
                    // remove element of array
                    if (target < (*this).nElementTotal[start]) {
                        int i;
                        (*this).pElement[start][target] = (*this).pElement[start][(*this).nElementTotal[start]];
                        i = 0;
                        while (i <= (*this).nElementTotal[start]) {
                            if ((*this).pElement[start][i]->small == (*this).nElementTotal[start]) {
                                (*this).pElement[start][i]->small = target;
                            }
                            if ((*this).pElement[start][i]->big == (*this).nElementTotal[start]) {
                                (*this).pElement[start][i]->big = target;
                            }
                            i++;
                        }
                        if ((*this).nElementStart[start] == (*this).nElementTotal[start]) {
                            (*this).nElementStart[start] = target;
                        }
                    }
                    break;
                }
                parent = target;
                parent_element = (*this).pElement[start][parent];
                if (cmp < 0) {
                    // small
                    target = target_element->small;
                } else {
                    // big
                    target = target_element->big;
                }
                if (target < 0) {
                    // ** do nothing (key not found) **
                    break;
                }
                target_element = (*this).pElement[start][target];
            }
        }
    }

    return true;
}


/*! Convert Keys data to serialized string.
    @param void
    @return Serialized string
 */
String &Keys::toString() {
    long m, i;
    String tmp_str1, tmp_str2;

    if ((*this).tmpString == (String *)NULL) {
        (*this).tmpString = new String;
    }
    *((*this).tmpString) = "";

    m = (*this).max();
    (*((*this).tmpString)).sprintf("# %ld key(s)\n", m);
    for (i = 0; i < m; i++) {
        *((*this).tmpString) += (*this).key(i);
        tmp_str1 = (*this).value(i).trim();
        tmp_str2 = (*this).value(i).escapeQuote();
        if (tmp_str1 == tmp_str2) {
            *((*this).tmpString) += "=";
            *((*this).tmpString) += tmp_str1;
            *((*this).tmpString) += "\n";
        } else {
            *((*this).tmpString) += "=\"";
            *((*this).tmpString) += tmp_str2;
            *((*this).tmpString) += "\"\n";
        }
    }

    tmp_str1.clear();
    tmp_str2.clear();

    return *((*this).tmpString);
}


/*! Set Keys structure by serialized string.
    @param str    String to set.
    @retval true  success
    @retval false failure
 */
bool Keys::setString(const String &str) {
    String line, key, value, tmp_str;
    long len, i, j, k;

    (*this).clear();

    len = str.len();
    if (len < 0L) {
        return false;
    }

    for (i = 0L; i < len; i++) {
        for (j = 0L; i + j < len; j++) {
            if (str[i + j] == '\n') {
                break;
            }
        }
        if (0L < j) {
            line = str.mid(i, j).trim();
            if (0L < line.len() && line[0] != '#' && 0 < (k = line.searchChar('='))) {
                String p, p1;
                p = line;
                p1 = p.left(k).trim();
                key = p1;
                p1 = p.mid(k+1).trim();
                value = p1;
                if (2L <= value.len() && value[0] == '"' && value[value.len()-1] == '"') {
                    tmp_str = value.mid(1, value.len()-2);
                    value = tmp_str.unescapeQuote();
                    tmp_str.clear();
                }
                (*this).addKey(key, value);
            }
            line.clear();
            i += j;
        }
    }

    return true;
}


/*! Read structured text file into a Keys.
    @param filename  File name to read.
    @retval true  success
    @retval false failure
 */
bool Keys::loadFile(const String &filename) {
    return (*this).loadFile((const char *)(filename.c_str()));
}


/*! Read structured text file into a Keys.
    @param filename  File name to read.
    @retval true  success
    @retval false failure
 */
bool Keys::loadFile(const char *filename) {
    String str, line, key, value, tmp_str;
    long len, i, j, k;

    (*this).clear();

    len = str.loadFile(filename);
    if (len < 0L) {
        return false;
    }

    for (i = 0L; i < len; i++) {
        for (j = 0L; i + j < len; j++) {
            if (str[i + j] == '\n') {
                break;
            }
        }
        if (0L < j) {
            line = str.mid(i, j).trim();
            if (0L < line.len() && line[0] != '#' && 0 < (k = line.searchChar('='))) {
                String p, p1;
                p = line;
                p1 = p.left(k).trim();
                key = p1;
                p1 = p.mid(k+1).trim();
                value = p1;
                if (2L <= value.len() && value[0] == '"' && value[value.len()-1] == '"') {
                    tmp_str = value.mid(1, value.len()-2);
                    value = tmp_str.unescapeQuote();
                    tmp_str.clear();
                }
                (*this).addKey(key, value);
            }
            line.clear();
            i += j;
        }
    }

    str.clear();

    return true;
}

/*! Write structured text file of serialized Keys.
    @param filename  File name to wtite.
    @retval true  success
    @retval false failure
 */
bool Keys::saveFile(const String &filename) const {
    return (*this).saveFile((const char *)(filename.c_str()));
}


/*! Write structured text file of serialized Keys.
    @param filename  File name to wtite.
    @retval true  success
    @retval false failure
 */
bool Keys::saveFile(const char *filename) const {
    long m, i;
    String str, tmp_str1, tmp_str2;
    bool ret;

    m = (*this).max();
    str.sprintf("# %ld key(s)\n", m);
    for (i = 0; i < m; i++) {
        str += (*this).key(i);
        tmp_str1 = (*this).value(i).trim();
        tmp_str2 = (*this).value(i).escapeQuote();
        if (tmp_str1 == tmp_str2) {
            str += "=";
            str += tmp_str1;
            str += "\n";
        } else {
            str += "=\"";
            str += tmp_str2;
            str += "\"\n";
        }
    }

    ret = str.saveFile(filename);

    tmp_str1.clear();
    tmp_str2.clear();
    str.clear();

    return ret;
}

} // namespace apolloron
