StelJsonParser.cpp   StelJsonParser.cpp 
skipping to change at line 21 skipping to change at line 21
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, U SA. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, U SA.
*/ */
#include "StelJsonParser.hpp" #include "StelJsonParser.hpp"
#include <QDebug> #include <QDebug>
#include <QBuffer>
#include <stdexcept> #include <stdexcept>
void skipJson(QIODevice& input) class StelJsonParserInstance
{ {
// There is a weakness in this code -- it will cause any standalone public:
'/' to be absorbed. StelJsonParserInstance(QIODevice* ain) : input(ain), hasNextChar(fal
char c; se)
while (input.getChar(&c))
{ {
if (QChar(c).isSpace() || c=='\n') cur = buffer;
{ last = cur;
continue; }
} inline void skipJson();
else inline bool tryReadChar(char c);
inline bool skipAndConsumeChar(char r);
QByteArray readString();
QVariant readOther();
QVariant parse();
private:
QIODevice* input;
char nextChar;
bool hasNextChar;
#define BUFSIZESTATIC 65536
char buffer[BUFSIZESTATIC];
char* last;
char* cur;
inline bool getNextFromBuffer(char *c)
{
if (cur==last)
{ {
if (c!='/') if (cur==buffer-1) // End of file
return false;
last = buffer + input->read(buffer, BUFSIZESTATIC);
if (last==buffer)
{ {
input.ungetChar(c); cur = buffer-1;
return; last = cur;
return false;
} }
cur = buffer;
}
*c = *cur;
++cur;
return true;
}
if (!input.getChar(&c)) inline bool getChar(char* c)
return; {
if (hasNextChar)
{
hasNextChar=false;
*c = nextChar;
return true;
}
return getNextFromBuffer(c);
}
if (c=='/') inline void ungetChar(char c)
{ {
input.readLine(); nextChar=c;
} hasNextChar=true;
else }
{
// We have a problem, we removed an '/'.. inline void skipLine()
qWarning() << "I removed a '/' while parsing {
JSON file."; if (hasNextChar && nextChar=='\n')
input.ungetChar(c); {
hasNextChar=false;
return;
}
hasNextChar=false;
char c;
while (getNextFromBuffer(&c) && c!='\n')
{}
}
inline bool atEnd()
{
return cur==buffer-1;
}
};
void StelJsonParserInstance::skipJson()
{
// There is a weakness in this code -- it will cause any standalone
'/' to be absorbed.
char c;
while (getChar(&c))
{
switch (c)
{
case ' ':
case '\t':
case '\n':
case '\r':
break;
case '/':
{
if (!getChar(&c))
return;
if (c=='/')
skipLine();
else
{
// We have a problem, we rem
oved a '/'.. This should never happen with properly formatted JSON though
throw std::runtime_error(qPr
intable(QString("Unexpected '/%1' in the JSON content").arg(c)));
}
}
break;
default:
ungetChar(c);
return; return;
}
} }
} }
} }
bool tryReadChar(QIODevice& input, char c) bool StelJsonParserInstance::tryReadChar(char c)
{ {
char r; char r;
if (!input.getChar(&r)) if (!getChar(&r))
return false; return false;
if (r == c) if (r == c)
return true; return true;
input.ungetChar(r); ungetChar(r);
return false; return false;
} }
QString readString(QIODevice& input) bool StelJsonParserInstance::skipAndConsumeChar(char r)
{
char c;
while (getChar(&c))
{
switch (c)
{
case ' ':
case '\t':
case '\n':
case '\r':
break;
case '/':
{
if (!getChar(&c))
throw std::runtime_error(qPr
intable(QString("Unexpected '/%1' in the JSON content").arg(c)));
if (c=='/')
skipLine();
else
{
// We have a problem, we rem
oved a '/'.. This should never happen with properly formatted JSON though
throw std::runtime_error(qPr
intable(QString("Unexpected '/%1' in the JSON content").arg(c)));
}
}
break;
default:
if (r==c)
return true;
ungetChar(c);
return false;
}
}
return false;
}
// Read a string without the initial "
QByteArray StelJsonParserInstance::readString()
{ {
QByteArray name; QByteArray name;
char c; char c;
input.getChar(&c); while (getChar(&c))
if (c!='\"') {
throw std::runtime_error("Expected '\"' at beginning of stri switch (c)
ng"); {
for (;;) case '\"':
{ return name;
input.getChar(&c); case '\\':
if (c=='\\') {
{ getChar(&c);
input.getChar(&c); // case '\"': break;
// case '\"': break; // case '\\': break;
// case '\\': break; // case '/': break;
// case '/': break; if (c=='b') c='\b';
if (c=='b') c='\b'; if (c=='f') c='\f';
if (c=='f') c='\f'; break; if (c=='n') c='\n';
if (c=='n') c='\n'; break; if (c=='r') c='\r';
if (c=='r') c='\r'; break; if (c=='t') c='\t';
if (c=='t') c='\t'; break; if (c=='u') {qWarning() << "don't support \\
if (c=='u') qWarning() << "don't support \\uxxxx cha uxxxx char"; continue;}
r"; break; break;
} }
if (c=='\"') default:
return QString(name); name+=c;
name+=c; }
if (input.atEnd())
throw std::runtime_error(qPrintable(QString("End of
file before end of string: "+name)));
} }
Q_ASSERT(0); if (atEnd())
throw std::runtime_error(qPrintable(QString("End of file bef
ore end of string: "+name)));
throw std::runtime_error(qPrintable(QString("Read error before end o
f string: "+name)));
return ""; return "";
} }
QVariant readOther(QIODevice& input) QVariant StelJsonParserInstance::readOther()
{ {
QByteArray str; QByteArray str;
char c; char c;
while (input.getChar(&c)) while (getChar(&c))
{ {
if (QChar(c).isSpace() || c==']' || c=='}' || c==',') if (c==' ' || c==',' || c=='\n' || c=='\r' || c==']' || c==' \t' || c=='}')
{ {
input.ungetChar(c); ungetChar(c);
break; break;
} }
str+=c; str+=c;
} }
QTextStream ts(&str); bool ok;
QString s; const int i = str.toInt(&ok);
ts >> s; if (ok)
if (s=="true") return i;
const double d = str.toDouble(&ok);
if (ok)
return d;
if (str=="true")
return QVariant(true); return QVariant(true);
if (s=="false") if (str=="false")
return QVariant(false); return QVariant(false);
if (s=="null") if (str=="null")
return QVariant(); return QVariant();
QVariant v(s); throw std::runtime_error(qPrintable(QString("Invalid JSON value: \""
bool ok=false; )+str+"\""));
int i = v.toInt(&ok);
if (ok) return i;
double d = v.toDouble(&ok);
if (ok)
return d;
return v;
} }
// Parse the given input stream // Parse the given input stream
QVariant StelJsonParser::parse(QIODevice& input) const QVariant StelJsonParserInstance::parse()
{ {
skipJson(input); skipJson();
if (tryReadChar(input, '{')) char r;
if (!getChar(&r))
return QVariant();
switch (r)
{ {
// We've got an object (a tuple) case '{':
QVariantMap map; {
skipJson(input); // We've got an object (a tuple)
if (tryReadChar(input, '}')) QVariantMap map;
if (skipAndConsumeChar('}'))
return map;
for (;;)
{
if (!skipAndConsumeChar('\"'))
{
char cc=0;
getChar(&cc);
throw std::runtime_error(qPrintable(
QString("Expected '\"' at beginning of string, found: '%1' (ASCII %2)").arg
(cc).arg((int)(cc))));
}
const QByteArray& ar = readString();
const QString& key = QString::fromUtf8(ar.co
nstData(), ar.size());
if (!skipAndConsumeChar(':'))
throw std::runtime_error(qPrintable(
QString("Expected ':' after a member name: ")+key));
skipJson();
map.insert(key, parse());
if (!skipAndConsumeChar(','))
break;
}
if (!skipAndConsumeChar('}'))
throw std::runtime_error("Expected '}' to cl
ose an object");
return map; return map;
for (;;) }
case '[':
{ {
skipJson(input); // We've got an array (a vector)
QString key = readString(input); QVariantList list;
skipJson(input); if (skipAndConsumeChar(']'))
if (!tryReadChar(input, ':')) return list;
throw std::runtime_error(qPrintable(QString(
"Expected ':' after a member name: ")+key));
skipJson(input);
map.insert(key, parse(input));
skipJson(input);
if (!tryReadChar(input, ',')) for (;;)
break; {
} list.append(parse());
if (!skipAndConsumeChar(','))
break;
}
skipJson(input); if (!skipAndConsumeChar(']'))
throw std::runtime_error("Expected ']' to cl
ose an array");
if (!tryReadChar(input, '}'))
throw std::runtime_error("Expected '}' to close an o
bject");
return map;
}
else if (tryReadChar(input, '['))
{
// We've got an array (a vector)
QVariantList list;
skipJson(input);
if (tryReadChar(input, ']'))
return list; return list;
}
for (;;) case '\"':
{ {
list.append(parse(input)); // We've got a string
skipJson(input); const QByteArray& ar = readString();
if (!tryReadChar(input, ',')) return QString::fromUtf8(ar.constData(), ar.size());
break; }
default:
{
ungetChar(r);
return readOther();
} }
skipJson(input);
if (!tryReadChar(input, ']'))
throw std::runtime_error("Expected ']' to close an a
rray");
return list;
}
else if (tryReadChar(input, '\"'))
{
// We've got a string
input.ungetChar('\"');
return readString(input);
} }
return readOther(input);
} }
// Serialize the passed QVariant as JSON into the output QIODevice // Serialize the passed QVariant as JSON into the output QIODevice
void StelJsonParser::write(const QVariant& v, QIODevice& output, int indent Level) const void StelJsonParser::write(const QVariant& v, QIODevice* output, int indent Level)
{ {
switch (v.type()) switch (v.type())
{ {
case QVariant::Bool: case QVariant::Bool:
output.write(v.toBool()==true ? "true" : "false"); output->write(v.toBool()==true ? "true" : "false");
break; break;
case QVariant::Invalid: case QVariant::Invalid:
output.write("null"); output->write("null");
break; break;
case QVariant::ByteArray:
{
QByteArray s(v.toByteArray());
s.replace('\"', "\\\"");
s.replace('\b', "\\b");
s.replace('\n', "\\n");
s.replace('\f', "\\f");
s.replace('\r', "\\r");
s.replace('\t', "\\t");
output->write("\"" + s + "\"");
break;
}
case QVariant::String: case QVariant::String:
{ {
QString s(v.toString()); QString s(v.toString());
s.replace('\"', "\\\""); s.replace('\"', "\\\"");
//s.replace('\\', "\\\\");
//s.replace('/', "\\/");
s.replace('\b', "\\b"); s.replace('\b', "\\b");
s.replace('\n', "\\n"); s.replace('\n', "\\n");
s.replace('\f', "\\f"); s.replace('\f', "\\f");
s.replace('\r', "\\r"); s.replace('\r', "\\r");
s.replace('\t', "\\t"); s.replace('\t', "\\t");
output.write(QString("\"%1\"").arg(s).toUtf8()); output->write(QString("\"%1\"").arg(s).toUtf8());
break; break;
} }
case QVariant::Int: case QVariant::Int:
case QVariant::Double: case QVariant::Double:
output.write(v.toString().toUtf8()); output->write(v.toString().toUtf8());
break; break;
case QVariant::List: case QVariant::List:
{ {
output.putChar('['); output->putChar('[');
const QVariantList& l = v.toList(); const QVariantList& l = v.toList();
for (int i=0;i<l.size();++i) for (int i=0;i<l.size();++i)
{ {
// Break line if we start an JSON Object for nice looking // Break line if we start an JSON Object for nice looking
if (l.at(i).type()==QVariant::Map) if (l.at(i).type()==QVariant::Map)
output.putChar('\n'); output->putChar('\n');
write(l.at(i), output, indentLevel); write(l.at(i), output, indentLevel);
if (i!=l.size()-1) if (i!=l.size()-1)
output.write(", "); output->write(", ");
} }
output.putChar(']'); output->putChar(']');
break; break;
} }
case QVariant::Map: case QVariant::Map:
{ {
const QByteArray prepend(indentLevel, '\t'); const QByteArray prepend(indentLevel, '\t');
output.write(prepend); output->write(prepend);
output.write("{\n"); output->write("{\n");
++indentLevel; ++indentLevel;
const QVariantMap& m = v.toMap(); const QVariantMap& m = v.toMap();
int j =0; int j =0;
for (QVariantMap::ConstIterator i=m.begin();i!=m.end ();++i) for (QVariantMap::ConstIterator i=m.begin();i!=m.end ();++i)
{ {
output.write(prepend); output->write(prepend);
output.write("\t\""); output->write("\t\"");
output.write(i.key().toUtf8()); output->write(i.key().toUtf8());
output.write("\": "); output->write("\": ");
// Break line if we start an JSON Object for nice looking // Break line if we start an JSON Object for nice looking
if (i.value().type()==QVariant::Map) if (i.value().type()==QVariant::Map)
output.putChar('\n'); output->putChar('\n');
write(i.value(), output, indentLevel); write(i.value(), output, indentLevel);
if (++j!=m.size()) if (++j!=m.size())
output.putChar(','); output->putChar(',');
output.putChar('\n'); output->putChar('\n');
} }
output.write(prepend); output->write(prepend);
output.write("}"); output->write("}");
--indentLevel; --indentLevel;
break; break;
} }
default: default:
qWarning() << "Cannot serialize QVariant of type " < output->write("null");
< v.typeName() << " in JSON"; //qWarning() << "Cannot serialize QVariant of type "
<< v.typeName() << " in JSON";
break; break;
} }
}
QByteArray StelJsonParser::write(const QVariant& jsonObject, int indentLeve
l)
{
QByteArray ar;
QBuffer buf(&ar);
buf.open(QIODevice::WriteOnly);
StelJsonParser::write(jsonObject, &buf, indentLevel);
buf.close();
return ar;
}
QVariant StelJsonParser::parse(QIODevice* input)
{
StelJsonParserInstance parser(input);
return parser.parse();
}
QVariant StelJsonParser::parse(const QByteArray& aar)
{
QByteArray ar = aar;
QBuffer buf(&ar);
buf.open(QIODevice::ReadOnly);
QVariant v = StelJsonParser::parse(&buf);
buf.close();
return v;
}
JsonListIterator::JsonListIterator(QIODevice* input)
{
parser = new StelJsonParserInstance(input);
if (!parser->skipAndConsumeChar('['))
{
throw std::runtime_error("Expected '[' to start a list itera
tor");
}
ahasNext = !parser->skipAndConsumeChar(']');
}
JsonListIterator::~JsonListIterator()
{
Q_ASSERT(parser);
delete parser;
parser=NULL;
}
QVariant JsonListIterator::next()
{
QVariant ret = parser->parse();
ahasNext = parser->skipAndConsumeChar(',');
if (!ahasNext)
{
if (!parser->skipAndConsumeChar(']'))
throw std::runtime_error("Expected ']' to end a list
iterator");
}
return ret;
} }
 End of changes. 56 change blocks. 
154 lines changed or deleted 353 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/