aboutsummaryrefslogtreecommitdiff
path: root/libcontextsubscriber/src/nanoxml.cpp
blob: d896450a71d2177d6ad1efe9fb2f427d03e9ee8b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
 * Copyright (C) 2009 Nokia Corporation.
 *
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include "nanoxml.h"
#include <QDebug>

/*!
    \class NanoXml

    \brief Parses XML files into an association tree, which serves as
    a nano document object model.

    This class is not exported in the public API. It's used to parse a
    formatted XML into a nanodom tree. To learn about the nano dom
    please read the documentation on the desktop types.

    Short overview - the following XML is being turned into the
    following tree structure:

    \code
    <key name="Example.Random" type="string">
        <doc>A random property.</doc>
    </key>

    ['key',
        ['name', 'Example.Random' ],
        ['type', 'string' ],
        ['doc', 'A random property.']
    ]
    \endcode

    Another example:

    \code
    <key name="Example.Random">
        <type>
            <list type="number"/>
        </type>
    </key>

    ['key',
        ['name', 'Example.Random' ],
        ['type',
            ['list', ['type', 'number' ] ]
        ]
    ]
    \endcode
*/

/// Constructor. Creates a new nanodom tree reading XML data from path. After creating
/// the object you should check the didFail to see if parsing succeded.
NanoXml::NanoXml(const QString& path)
{
    QFile f(path);
    initAndParse(&f);
}

NanoXml::NanoXml(QIODevice *ioDevice)
{
    initAndParse(ioDevice);
}

void NanoXml::initAndParse(QIODevice *ioDevice)
{
    current = NULL;
    QXmlInputSource source(ioDevice);
    QXmlSimpleReader reader;
    reader.setContentHandler(this);
    failed = !reader.parse(source);
}

/// Destructor.
NanoXml::~NanoXml()
{
    delete current;
}

/// Parser internal. Add a value (item) \a v to the current list on the stack.
void NanoXml::addValue(const QString& v)
{
    current->append(QVariant(v));
}

/// Parser internal. Creates a new list and pushes it to the top of the stack.
void NanoXml::pushList()
{
    if (current)
        stack.push(current);

    current = new QList<QVariant> ();
}

/// Parser internal. Pops one list from the stack. Closes the list and attaches
/// it to the previoius list on the stack.
void NanoXml::popList()
{
    if (current == NULL) {
        // FIXME: Warning goes here
        return;
    }

    QVariant currentListAsVariant(*current);
    delete current;

    if (stack.count() > 0) {
        current = stack.pop();
        current->append(currentListAsVariant);
    } else {
        // End of stack unwinding. We're done.
        current = NULL;
        rootVariant = currentListAsVariant;
    }
}

/// Called when a namespace prefix mapping starts. We use this to get the
/// xml version.
bool NanoXml::startPrefixMapping(const QString &prefix, const QString &uri)
{
    nspace = uri;
    return true;
}

/// Called by the XML parser when parsing starts.
bool NanoXml::startElement(const QString&, const QString&, const QString &name, const QXmlAttributes &attrs)
{
    pushList();
    addValue(name);

    for (int i = 0; i< attrs.count(); i++) {
        pushList();
        addValue(attrs.localName(i));
        addValue(attrs.value(i));
        popList();
    }

    return true;
}

/// Called by the XML parser when parsing starts.
bool NanoXml::endElement(const QString&, const QString&, const QString &name)
{
    popList();
    return true;
}

/// Called by the XML parser when parsing starts.
bool NanoXml::characters(const QString &chars)
{
    QString trimmed = chars.trimmed();

    if (trimmed != "")
        addValue(trimmed);

    return true;
}

/// Returns the namespace URI of the parsed (source) XML document. Empty if
/// it wasn't specified.
const QString NanoXml::namespaceUri()
{
    return nspace;
}

/// Returns true if parsing failed. Fals otherwise. Use it to check if the nanodom
/// tree is fine and usable.
bool NanoXml::didFail()
{
    return failed;
}


AssocTree NanoXml::result()
{
    return AssocTree(rootVariant);
}