XLIFFFile*
XLIFFFile::Parse(const string& filename)
{
    XLIFFFile* result = new XLIFFFile();

    XMLNode* root = NodeHandler::ParseFile(filename, XMLNode::PRETTY);
    if (root == NULL) {
        return NULL;
    }

    // <file>
    vector<XMLNode*> files = root->GetElementsByName(XLIFF_XMLNS, "file");
    for (size_t i=0; i<files.size(); i++) {
        XMLNode* file = files[i];

        string datatype = file->GetAttribute("", "datatype", "");
        string originalFile = file->GetAttribute("", "original", "");

        Configuration sourceConfig;
        sourceConfig.locale = file->GetAttribute("", "source-language", "");
        result->m_sourceConfig = sourceConfig;

        Configuration targetConfig;
        targetConfig.locale = file->GetAttribute("", "target-language", "");
        result->m_targetConfig = targetConfig;

        result->m_currentVersion = file->GetAttribute("", "build-num", "");
        result->m_oldVersion = "old";

        // <body>
        XMLNode* body = get_unique_node(file, XLIFF_XMLNS, "body", true);
        if (body == NULL) continue;

        // <trans-unit>
        vector<XMLNode*> transUnits = body->GetElementsByName(XLIFF_XMLNS, "trans-unit");
        for (size_t j=0; j<transUnits.size(); j++) {
            XMLNode* transUnit = transUnits[j];

            string rawID = transUnit->GetAttribute("", "id", "");
            if (rawID == "") {
                transUnit->Position().Error("<trans-unit> tag requires an id");
                continue;
            }
            string id;
            int index;

            if (!StringResource::ParseTypedID(rawID, &id, &index)) {
                transUnit->Position().Error("<trans-unit> has invalid id '%s'\n", rawID.c_str());
                continue;
            }

            // <source>
            XMLNode* source = get_unique_node(transUnit, XLIFF_XMLNS, "source", false);
            if (source != NULL) {
                XMLNode* node = source->Clone();
                node->SetPrettyRecursive(XMLNode::EXACT);
                result->AddStringResource(StringResource(source->Position(), originalFile,
                            sourceConfig, id, index, node, CURRENT_VERSION,
                            result->m_currentVersion));
            }

            // <target>
            XMLNode* target = get_unique_node(transUnit, XLIFF_XMLNS, "target", false);
            if (target != NULL) {
                XMLNode* node = target->Clone();
                node->SetPrettyRecursive(XMLNode::EXACT);
                result->AddStringResource(StringResource(target->Position(), originalFile,
                            targetConfig, id, index, node, CURRENT_VERSION,
                            result->m_currentVersion));
            }

            // <alt-trans>
            XMLNode* altTrans = get_unique_node(transUnit, XLIFF_XMLNS, "alt-trans", false);
            if (altTrans != NULL) {
                // <source>
                XMLNode* altSource = get_unique_node(altTrans, XLIFF_XMLNS, "source", false);
                if (altSource != NULL) {
                    XMLNode* node = altSource->Clone();
                    node->SetPrettyRecursive(XMLNode::EXACT);
                    result->AddStringResource(StringResource(altSource->Position(),
                                originalFile, sourceConfig, id, index, node, OLD_VERSION,
                                result->m_oldVersion));
                }

                // <target>
                XMLNode* altTarget = get_unique_node(altTrans, XLIFF_XMLNS, "target", false);
                if (altTarget != NULL) {
                    XMLNode* node = altTarget->Clone();
                    node->SetPrettyRecursive(XMLNode::EXACT);
                    result->AddStringResource(StringResource(altTarget->Position(),
                                originalFile, targetConfig, id, index, node, OLD_VERSION,
                                result->m_oldVersion));
                }
            }
        }
    }
    delete root;
    return result;
}
int
read_settings(const string& filename, map<string,Settings>* result, const string& rootDir)
{
    XMLNode* root = NodeHandler::ParseFile(filename, XMLNode::PRETTY);
    if (root == NULL) {
        SourcePos(filename, -1).Error("Error reading file.");
        return 1;
    }

    // <configuration>
    vector<XMLNode*> configNodes = root->GetElementsByName("", "configuration");
    const size_t I = configNodes.size();
    for (size_t i=0; i<I; i++) {
        const XMLNode* configNode = configNodes[i];

        Settings settings;
        settings.id = configNode->GetAttribute("", "id", "");
        if (settings.id == "") {
            configNode->Position().Error("<configuration> needs an id attribute.");
            delete root;
            return 1;
        }

        settings.oldVersion = configNode->GetAttribute("", "old-cl", "");

        settings.currentVersion = configNode->GetAttribute("", "new-cl", "");
        if (settings.currentVersion == "") {
            configNode->Position().Error("<configuration> needs a new-cl attribute.");
            delete root;
            return 1;
        }

        // <app>
        vector<XMLNode*> appNodes = configNode->GetElementsByName("", "app");

        const size_t J = appNodes.size();
        for (size_t j=0; j<J; j++) {
            const XMLNode* appNode = appNodes[j];

            string dir = appNode->GetAttribute("", "dir", "");
            if (dir == "") {
                appNode->Position().Error("<app> needs a dir attribute.");
                delete root;
                return 1;
            }

            settings.apps.push_back(dir);
        }

        // <reject>
        vector<XMLNode*> rejectNodes = configNode->GetElementsByName("", "reject");

        const size_t K = rejectNodes.size();
        for (size_t k=0; k<K; k++) {
            const XMLNode* rejectNode = rejectNodes[k];

            Reject reject;

            reject.file = rejectNode->GetAttribute("", "file", "");
            if (reject.file == "") {
                rejectNode->Position().Error("<reject> needs a file attribute.");
                delete root;
                return 1;
            }
            string f =  reject.file;
            reject.file = rootDir;
            reject.file += '/';
            reject.file += f;

            reject.name = rejectNode->GetAttribute("", "name", "");
            if (reject.name == "") {
                rejectNode->Position().Error("<reject> needs a name attribute.");
                delete root;
                return 1;
            }

            reject.comment = trim_string(rejectNode->CollapseTextContents());

            settings.reject.push_back(reject);
        }

        (*result)[settings.id] = settings;
    }

    delete root;
    return 0;
}