~ubuntu-branches/ubuntu/precise/stellarium/precise

« back to all changes in this revision

Viewing changes to src/core/StelJsonParser.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Cédric Delfosse
  • Date: 2009-03-13 20:07:22 UTC
  • mfrom: (1.1.8 upstream) (4.1.2 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090313200722-l66s4zy2s3e8up0s
Tags: 0.10.2-1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2008 Fabien Chereau
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU General Public License
 
6
 * as published by the Free Software Foundation; either version 2
 
7
 * of the License, or (at your option) any later version.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 * GNU General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software
 
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
17
 */
 
18
 
 
19
#include "StelJsonParser.hpp"
 
20
#include <QDebug>
 
21
#include <stdexcept>
 
22
 
 
23
void skipJson(QIODevice& input)
 
24
{
 
25
        // There is a weakness in this code -- it will cause any standalone '/' to be absorbed.
 
26
        char c;
 
27
        while (input.getChar(&c))
 
28
        {
 
29
                if (QChar(c).isSpace() || c=='\n')
 
30
                {
 
31
                        continue;
 
32
                }
 
33
                else
 
34
                {
 
35
                        if (c!='/')
 
36
                        {
 
37
                                input.ungetChar(c);
 
38
                                return;
 
39
                        }
 
40
 
 
41
                        if (!input.getChar(&c))
 
42
                                return;
 
43
 
 
44
                        if (c=='/')
 
45
                        {
 
46
                                input.readLine();
 
47
                        }
 
48
                        else
 
49
                        {
 
50
                                // We have a problem, we removed an '/'..
 
51
                                qWarning() << "I removed a '/' while parsing JSON file.";
 
52
                                input.ungetChar(c);
 
53
                                return;
 
54
                        }
 
55
                }
 
56
        }
 
57
}
 
58
 
 
59
bool tryReadChar(QIODevice& input, char c)
 
60
{
 
61
        char r;
 
62
        if (!input.getChar(&r))
 
63
                return false;
 
64
 
 
65
        if (r == c)
 
66
                return true;
 
67
                
 
68
        input.ungetChar(r);
 
69
        return false;
 
70
}
 
71
 
 
72
QString readString(QIODevice& input)
 
73
{
 
74
        QByteArray name;
 
75
        char c;
 
76
        input.getChar(&c);
 
77
        if (c!='\"')
 
78
                throw std::runtime_error("Expected '\"' at beginning of string");
 
79
        for (;;)
 
80
        {
 
81
                input.getChar(&c);
 
82
                if (c=='\\')
 
83
                {
 
84
                        input.getChar(&c);
 
85
        //                      case '\"': break;
 
86
        //                      case '\\': break;
 
87
        //                      case '/': break;
 
88
                        if (c=='b') c='\b';
 
89
                        if (c=='f') c='\f'; break;
 
90
                        if (c=='n') c='\n'; break;
 
91
                        if (c=='r') c='\r'; break;
 
92
                        if (c=='t') c='\t'; break;
 
93
                        if (c=='u') qWarning() << "don't support \\uxxxx char"; break;
 
94
                }
 
95
                if (c=='\"')
 
96
                        return QString(name);
 
97
                name+=c;
 
98
                if (input.atEnd())
 
99
                        throw std::runtime_error(qPrintable(QString("End of file before end of string: "+name)));
 
100
        }
 
101
        Q_ASSERT(0);
 
102
        return "";
 
103
}
 
104
 
 
105
QVariant readOther(QIODevice& input)
 
106
{
 
107
        QByteArray str;
 
108
        char c;
 
109
        while (input.getChar(&c))
 
110
        {
 
111
                if (QChar(c).isSpace() || c==']' || c=='}' || c==',')
 
112
                {
 
113
                        input.ungetChar(c);
 
114
                        break;
 
115
                }
 
116
                str+=c;
 
117
        }
 
118
        QTextStream ts(&str);
 
119
        QString s;
 
120
        ts >> s;
 
121
        if (s=="true")
 
122
                return QVariant(true);
 
123
        if (s=="false")
 
124
                return QVariant(false);
 
125
        if (s=="null")
 
126
                return QVariant();
 
127
        QVariant v(s);
 
128
        bool ok=false;
 
129
        int i = v.toInt(&ok);
 
130
        if (ok) return i;
 
131
        double d = v.toDouble(&ok);
 
132
        if (ok)
 
133
                return d;
 
134
        return v;
 
135
}
 
136
 
 
137
// Parse the given input stream
 
138
QVariant StelJsonParser::parse(QIODevice& input) const
 
139
{
 
140
        skipJson(input);
 
141
        
 
142
        if (tryReadChar(input, '{'))
 
143
        {
 
144
                // We've got an object (a tuple)
 
145
                QVariantMap map;
 
146
                skipJson(input);
 
147
                if (tryReadChar(input, '}'))
 
148
                        return map;
 
149
                for (;;)
 
150
                {
 
151
                        skipJson(input);
 
152
                        QString key = readString(input);
 
153
                        skipJson(input);
 
154
                        if (!tryReadChar(input, ':'))
 
155
                                throw std::runtime_error(qPrintable(QString("Expected ':' after a member name: ")+key));
 
156
                        
 
157
                        skipJson(input);
 
158
                        map.insert(key, parse(input));
 
159
                        skipJson(input);
 
160
                
 
161
                        if (!tryReadChar(input, ','))
 
162
                                break;
 
163
                }
 
164
        
 
165
                skipJson(input);
 
166
        
 
167
                if (!tryReadChar(input, '}'))
 
168
                        throw std::runtime_error("Expected '}' to close an object");
 
169
                return map;
 
170
        }
 
171
        else if (tryReadChar(input, '['))
 
172
        {
 
173
                // We've got an array (a vector)
 
174
                QVariantList list;
 
175
                skipJson(input);
 
176
                if (tryReadChar(input, ']'))
 
177
                        return list;
 
178
                
 
179
                for (;;)
 
180
                {
 
181
                        list.append(parse(input));
 
182
                        skipJson(input);
 
183
                        if (!tryReadChar(input, ','))
 
184
                                break;
 
185
                }
 
186
        
 
187
                skipJson(input);
 
188
        
 
189
                if (!tryReadChar(input, ']'))
 
190
                        throw std::runtime_error("Expected ']' to close an array");
 
191
                
 
192
                return list;
 
193
        }
 
194
        else if (tryReadChar(input, '\"'))
 
195
        {
 
196
                // We've got a string
 
197
                input.ungetChar('\"');
 
198
                return readString(input);
 
199
        }
 
200
        return readOther(input);
 
201
}
 
202
 
 
203
// Serialize the passed QVariant as JSON into the output QIODevice
 
204
void StelJsonParser::write(const QVariant& v, QIODevice& output, int indentLevel) const
 
205
{
 
206
        switch (v.type())
 
207
        {
 
208
                case QVariant::Bool:
 
209
                        output.write(v.toBool()==true ? "true" : "false");
 
210
                        break;
 
211
                case QVariant::Invalid:
 
212
                        output.write("null");
 
213
                        break;
 
214
                case QVariant::String:
 
215
                {
 
216
                        QString s(v.toString());
 
217
                        s.replace('\"', "\\\"");
 
218
                        //s.replace('\\', "\\\\");
 
219
                        //s.replace('/', "\\/");
 
220
                        s.replace('\b', "\\b");
 
221
                        s.replace('\n', "\\n");
 
222
                        s.replace('\f', "\\f");
 
223
                        s.replace('\r', "\\r");
 
224
                        s.replace('\t', "\\t");
 
225
                        output.write(QString("\"%1\"").arg(s).toUtf8());
 
226
                        break;
 
227
                }
 
228
                case QVariant::Int:
 
229
                case QVariant::Double:
 
230
                        output.write(v.toString().toUtf8());
 
231
                        break;
 
232
                case QVariant::List:
 
233
                {
 
234
                        output.putChar('[');
 
235
                        const QVariantList& l = v.toList();
 
236
                        for (int i=0;i<l.size();++i)
 
237
                        {
 
238
                                // Break line if we start an JSON Object for nice looking
 
239
                                if (l.at(i).type()==QVariant::Map)
 
240
                                        output.putChar('\n');
 
241
                                write(l.at(i), output, indentLevel);
 
242
                                if (i!=l.size()-1)
 
243
                                        output.write(", ");
 
244
                        }
 
245
                        output.putChar(']');
 
246
                        break;
 
247
                }
 
248
                case QVariant::Map:
 
249
                {
 
250
                        const QByteArray prepend(indentLevel, '\t');
 
251
                        output.write(prepend);
 
252
                        output.write("{\n");
 
253
                        ++indentLevel;
 
254
                        const QVariantMap& m = v.toMap();
 
255
                        int j =0;
 
256
                        for (QVariantMap::ConstIterator i=m.begin();i!=m.end();++i)
 
257
                        {
 
258
                                output.write(prepend);
 
259
                                output.write("\t\"");
 
260
                                output.write(i.key().toUtf8());
 
261
                                output.write("\": ");
 
262
                                // Break line if we start an JSON Object for nice looking
 
263
                                if (i.value().type()==QVariant::Map)
 
264
                                        output.putChar('\n');
 
265
                                write(i.value(), output, indentLevel);
 
266
                                if (++j!=m.size())
 
267
                                        output.putChar(',');
 
268
                                output.putChar('\n');
 
269
                        }
 
270
                        output.write(prepend);
 
271
                        output.write("}");
 
272
                        --indentLevel;
 
273
                        break;
 
274
                }
 
275
                default:
 
276
                        qWarning() << "Cannot serialize QVariant of type " << v.typeName() << " in JSON";
 
277
                        break;
 
278
        }
 
279
                
 
280
}