1
#include "msoxmlnodemodel.h"
2
#include "utils.h"
3
#include <QtXmlPatterns/QXmlNamePool>
4
#include <QtCore/QUrl>
5
#include <QDebug>
6
7
using namespace std;
8
9
/** This XML model spans one OLE stream file.
10
 * The root node corresponds to the file. The child elements of the root
11
 * correspond to streams.
12
 * Below the streams, the elements can correspond to introspectables or values.
13
 * Each Introspectable contains a colleciont of Introspectables and values.
14
 * Each value is represented by a value element and a value text field.
15
 * There is a tree of introspectables available. Values (QVariants) do not know
16
 * about their parents and have to have a the pointer to their introspectable of
17
 * stream as a parent. The additionalData() filed of the QXmlNodeindex should
18
 * contain this information.
19
 * The data() field is a pointer to the introspectable also for the stream.
20
 **/
21
namespace {
22
23
/** The field additionalData() in QXmlNodeModelIndex is used for storing four
24
 *  bits of information while keeping the field increasing in value with
25
 *  document order. The value of the field data() is the same for each node
26
 *  below another node that is not a numbers: field type, member number and
27
 *  index in the member. These fields are
28
 **/
29
30
class Node {
31
public:
32
    enum Type { Document, RootElement, Stream, Introspectable, ValueElement };
33
    const void* data;
34
    int parent;
35
    int prev;
36
    int next;
37
    int firstChild;
38
    Type type;
39
    int memberno;
40
    int arrayno;
41
42
    Node() :data(0), parent(-1), prev(-1), next(-1), firstChild(-1) {}
43
};
44
45
int
46
countItems(const Introspectable* i) {
47
    if (i == 0) return 0;
48
    const Introspection* is = i->getIntrospection();
49
    int n = 0;
50
    for (int j=0; j<is->numberOfMembers; ++j) {
51
        for (int k=0; k<is->numberOfInstances[j](i); ++k) {
52
            const Introspectable* ci = is->introspectable[j](i, k);
53
            if (ci) {
54
                n += 1 + countItems(ci);
55
            } else {
56
                n += 1; // empty value element
57
            }
58
        }
59
    }
60
    return n;
61
}
62
63
int
64
countItems(const QMap<QString, QSharedPointer<const Introspectable> >& streams){
65
    int n = 2;
66
    QMap<QString, QSharedPointer<const Introspectable> >::const_iterator it
67
        = streams.begin();
68
    while (it != streams.end()) {
69
        n += 1 + countItems(it.value().data());
70
        it++;
71
    }
72
    return n;
73
}
74
75
void
76
addIntrospectable(QVector<Node>& nodes, const Introspectable* i, int pos,
77
        int parent) {
78
    if (i == 0) return;
79
    const Introspection* is = i->getIntrospection();
80
    nodes[pos].parent = parent;
81
    nodes[pos].data = i;
82
    nodes[pos].type = Node::Introspectable;
83
    int prevp = -1;
84
    int p = pos+1;
85
    for (int j=0; j<is->numberOfMembers; ++j) {
86
        for (int k=0; k<is->numberOfInstances[j](i); ++k) {
87
            const Introspectable* ci = is->introspectable[j](i, k);
88
            if (nodes[pos].firstChild == -1) {
89
                nodes[pos].firstChild = p;
90
            }
91
            if (prevp != -1) {
92
                nodes[p].prev = prevp;
93
                nodes[prevp].next = p;
94
            }
95
            prevp = p;
96
            nodes[p].memberno = j;
97
            nodes[p].arrayno = k;
98
            if (ci) {
99
                addIntrospectable(nodes, ci, p, pos);
100
                p += 1 + countItems(ci);
101
            } else {
102
                nodes[p].parent = pos;
103
                nodes[p].data = i;
104
                nodes[p].type = Node::ValueElement;
105
                p += 1; // skip empty position
106
            }
107
        }
108
    }
109
}
110
111
QVector<Node>
112
createNodes(const QMap<QString, QSharedPointer<const Introspectable> >&
113
        streams) {
114
    QVector<Node> nodes(countItems(streams));
115
    nodes[0].parent = -1; nodes[0].prev = -1; nodes[0].next = -1;
116
    nodes[0].firstChild = 1; nodes[0].type = Node::Document;
117
    nodes[1].parent =  0; nodes[1].prev = -1; nodes[1].next = -1;
118
    nodes[1].firstChild = (streams.size()) ?2 :-1;
119
    nodes[1].type = Node::RootElement;
120
    int prevp = -1;
121
    int p = 2;
122
    QMap<QString, QSharedPointer<const Introspectable> >::const_iterator it
123
        = streams.begin();
124
    while (it != streams.end()) {
125
        const Introspectable* i = it.value().data();
126
        addIntrospectable(nodes, i, p, 1);
127
        if (prevp != -1) {
128
            nodes[p].prev = prevp;
129
            nodes[prevp].next = p;
130
        }
131
        nodes[p].type = Node::Stream;
132
        prevp = p;
133
        p += 1 + countItems(i);
134
        it++;
135
    }
136
    return nodes;
137
}
138
139
}
140
141
class MsoXmlNodeModel::Private {
142
public:
143
    QXmlNamePool namepool;
144
    QString filepath;
145
    const QMap<QString, QSharedPointer<const Introspectable> > streams;
146
    const QVector<Node> nodes;
147
    const QXmlName ppt;
148
    const QXmlName directory;
149
    const QXmlName file;
150
    const QXmlName olestream;
151
    const QXmlName olestreamdir;
152
    const QXmlName name;
153
    const QXmlName size;
154
    const QXmlName type;
155
156
    static const QMap<QString, QSharedPointer<const Introspectable> > read(
157
            const char* filepath) {
158
        QString file = QString::fromLocal8Bit(filepath);
159
        const QMap<QString, QByteArray> streams = readStreams(file);
160
        return parseStreams(streams);
161
    }
162
163
    Private(const QXmlNamePool& pool, const char* filepath_) :namepool(pool),
164
            filepath(filepath_),
165
            streams(read(filepath_)),
166
            nodes(createNodes(streams)),
167
            ppt(QXmlName(namepool, QLatin1String("ppt"))),
168
            directory(QXmlName(namepool, QLatin1String("directory"))),
169
            file(QXmlName(namepool, QLatin1String("file"))),
170
            olestream(QXmlName(namepool, QLatin1String("olestream"))),
171
            olestreamdir(QXmlName(namepool, QLatin1String("olestreamdir"))),
172
            name(QXmlName(namepool, QLatin1String("name"))),
173
            size(QXmlName(namepool, QLatin1String("size"))),
174
            type(QXmlName(namepool, QLatin1String("type"))) {
175
    }
176
177
    void getIndex(QAbstractXmlNodeModel::SimpleAxis axis, qint64& data,
178
            qint64& additionalData) {
179
        if (additionalData == 1) {
180
            additionalData = 0;
181
            if (axis != Parent) data = -1;
182
            return;
183
        }
184
        const Node& node = nodes[data];
185
        // additionalData = 0;
186
        if (axis == Parent) {
187
            data = node.parent;
188
            return;
189
        }
190
        if (axis == FirstChild) {
191
            if (node.type == Node::ValueElement) {
192
                additionalData = 1;
193
            } else {
194
                data = node.firstChild;
195
            }
196
            return;
197
        }
198
        data = (axis == NextSibling) ?node.next :node.prev;
199
    }
200
 
201
    QXmlName getName(qint64 n, qint64 a) {
202
        if (a) return type;
203
        const Node& node = nodes[n];
204
        const Introspectable* i
205
            = static_cast<const Introspectable*>(nodes[node.parent].data);
206
        const Introspection* si = i->getIntrospection();
207
        return QXmlName(namepool, si->names[node.memberno]);
208
    }
209
};
210
211
MsoXmlNodeModel::MsoXmlNodeModel(const QXmlNamePool& pool, const char* filepath)
212
        :d(new Private(pool, filepath)) {
213
}
214
MsoXmlNodeModel::~MsoXmlNodeModel() {
215
    delete d;
216
}
217
218
QVector<QXmlNodeModelIndex>
219
MsoXmlNodeModel::attributes(const QXmlNodeModelIndex& element) const {
220
    QVector<QXmlNodeModelIndex> v;
221
    const Node& node = d->nodes[element.data()];
222
    if (node.type == Node::Introspectable && element.additionalData() == 0) {
223
        v.append(createIndex(element.data(), 1));
224
    }
225
    return v;
226
}
227
228
QXmlNodeModelIndex
229
MsoXmlNodeModel::nextFromSimpleAxis(SimpleAxis axis,
230
        const QXmlNodeModelIndex& origin) const {
231
    qint64 data = -1;
232
    qint64 additionalData = 0;
233
    const Node& node = d->nodes[origin.data()];
234
    if (node.type == Node::Introspectable || node.type == Node::ValueElement) {
235
        data = origin.data();
236
        additionalData = origin.additionalData();
237
        d->getIndex(axis, data, additionalData);
238
    } else {
239
        switch (axis) {
240
            case Parent:          data = node.parent;     break;
241
            case FirstChild:      data = node.firstChild; break;
242
            case PreviousSibling: data = node.prev;       break;
243
            case NextSibling:     data = node.next;       break;
244
            default: break;
245
        }
246
    }
247
    return (data == -1)
248
        ?QXmlNodeModelIndex() :createIndex(data, additionalData);
249
}
250
251
QUrl
252
MsoXmlNodeModel::baseUri(const QXmlNodeModelIndex& n) const {
253
    return documentUri(n);
254
}
255
QXmlNodeModelIndex::DocumentOrder
256
MsoXmlNodeModel::compareOrder(const QXmlNodeModelIndex& ni1,
257
        const QXmlNodeModelIndex& ni2) const {
258
    if (ni1.data() < ni2.data()) return QXmlNodeModelIndex::Precedes;
259
    if (ni1.data() == ni2.data()) {
260
        if (ni1.additionalData() < ni2.additionalData())
261
            return QXmlNodeModelIndex::Precedes;
262
        if (ni1.additionalData() == ni2.additionalData())
263
            return QXmlNodeModelIndex::Is;
264
    }
265
    return QXmlNodeModelIndex::Follows;
266
}
267
QUrl
268
MsoXmlNodeModel::documentUri(const QXmlNodeModelIndex& n) const {
269
    return QUrl::fromLocalFile(d->filepath);
270
}
271
QXmlNodeModelIndex
272
MsoXmlNodeModel::elementById(const QXmlName& id) const {
273
    return QXmlNodeModelIndex();
274
}
275
QXmlNodeModelIndex::NodeKind
276
MsoXmlNodeModel::kind(const QXmlNodeModelIndex& ni) const {
277
    if (ni.data() == 0) return QXmlNodeModelIndex::Document;
278
    if (ni.additionalData() == 1) {
279
        const Node& node = d->nodes[ni.data()];
280
        if (node.type == Node::Introspectable) {
281
            return QXmlNodeModelIndex::Attribute;
282
        }
283
        return QXmlNodeModelIndex::Text;
284
    }
285
    return QXmlNodeModelIndex::Element; // no attributes yet
286
}
287
QXmlName
288
MsoXmlNodeModel::name(const QXmlNodeModelIndex& ni) const {
289
    const Node& node = d->nodes[ni.data()];
290
    const Introspectable* i = static_cast<const Introspectable*>(node.data);
291
    const Introspection* si = (i) ?i->getIntrospection() :0;
292
    switch (node.type) {
293
        case Node::Document: return d->name; // should not matter
294
        case Node::RootElement: return d->ppt; 
295
        case Node::Stream: return QXmlName(d->namepool, si->name);
296
        case Node::Introspectable:
297
            return d->getName(ni.data(), ni.additionalData());
298
        case Node::ValueElement:
299
            return d->getName(ni.data(), ni.additionalData());
300
        default: break;
301
    }
302
    return QXmlName();
303
}
304
QVector<QXmlName>
305
MsoXmlNodeModel::namespaceBindings(const QXmlNodeModelIndex& n) const {
306
    return QVector<QXmlName>();
307
}
308
QVector<QXmlNodeModelIndex>
309
MsoXmlNodeModel::nodesByIdref(const QXmlName& idref) const {
310
    return QVector<QXmlNodeModelIndex>();
311
}
312
QXmlNodeModelIndex
313
MsoXmlNodeModel::root(const QXmlNodeModelIndex& n) const {
314
    return createIndex((qint64)0);
315
}
316
QString
317
MsoXmlNodeModel::stringValue(const QXmlNodeModelIndex& n) const {
318
    return typedValue(n).toString();
319
}
320
QVariant
321
MsoXmlNodeModel::typedValue(const QXmlNodeModelIndex& n) const {
322
    if (n.additionalData() == 1) { //attribute or value type
323
        const Node& node = d->nodes[n.data()];
324
        const Introspectable* i
325
            = static_cast<const Introspectable*>(d->nodes[n.data()].data);
326
        const Introspection* si = i->getIntrospection();
327
        if (node.type == Node::Introspectable) {
328
            return si->name;
329
        }
330
        return si->value[node.memberno](i, node.arrayno);
331
    }
332
    return QVariant();
333
}