Ticket #11104: 0001-libshared-Introduce-JSON-parser-and-MessageBuilder.patch

File 0001-libshared-Introduce-JSON-parser-and-MessageBuilder.patch, 15.1 KB (added by waddlesplash, 10 years ago)

The parser.

  • new file headers/private/shared/Json.h

    From 38c80269c006817085d25e069427a7107cd0b639 Mon Sep 17 00:00:00 2001
    From: Augustin Cavalier <waddlesplash@gmail.com>
    Date: Mon, 4 Aug 2014 13:00:03 -0400
    Subject: [PATCH] libshared: Introduce JSON parser and MessageBuilder.
    
    Based on an earlier piece of source code of mine that parsed JSON into
    QObjects, this JSON parser creates a BMessage tree.
    
    Will be used by Stephan in HaikuDepot for communication with the web app.
    ---
     headers/private/shared/Json.h           |  33 ++++
     headers/private/shared/MessageBuilder.h |  54 ++++++
     src/kits/shared/Jamfile                 |   2 +
     src/kits/shared/Json.cpp                | 325 ++++++++++++++++++++++++++++++++
     src/kits/shared/MessageBuilder.cpp      | 208 ++++++++++++++++++++
     5 files changed, 622 insertions(+)
     create mode 100644 headers/private/shared/Json.h
     create mode 100644 headers/private/shared/MessageBuilder.h
     create mode 100644 src/kits/shared/Json.cpp
     create mode 100644 src/kits/shared/MessageBuilder.cpp
    
    diff --git a/headers/private/shared/Json.h b/headers/private/shared/Json.h
    new file mode 100644
    index 0000000..a3eccba
    - +  
     1/*
     2 * Copyright 2014, Augustin Cavalier (waddlesplash)
     3 * Distributed under the terms of the MIT License.
     4 */
     5#ifndef JSON_H
     6#define JSON_H
     7
     8#include <Message.h>
     9#include <String.h>
     10
     11namespace BPrivate {
     12
     13class BJson {
     14public:
     15            enum JsonObjectType {
     16                JSON_TYPE_MAP = '_JTM',
     17                JSON_TYPE_ARRAY = '_JTA'
     18            };
     19
     20public:
     21    static  status_t            Parse(BMessage& message, const char* JSON);
     22    static  status_t            Parse(BMessage& message, BString& JSON);
     23
     24private:
     25    static  BString             _Parser_ParseString(BString& JSON, int32& pos);
     26    static  double              _Parser_ParseNumber(BString& JSON, int32& pos);
     27    static  bool                _Parser_ParseConstant(BString& JSON, int32& pos,
     28                                                      const char* constant);
     29};
     30
     31} // namespace BPrivate
     32
     33#endif  // JSON_H
  • new file headers/private/shared/MessageBuilder.h

    diff --git a/headers/private/shared/MessageBuilder.h b/headers/private/shared/MessageBuilder.h
    new file mode 100644
    index 0000000..a5b5541
    - +  
     1/*
     2 * Copyright 2014, Augustin Cavalier (waddlesplash)
     3 * Distributed under the terms of the MIT License.
     4 */
     5#ifndef MESSAGE_BUILDER_H
     6#define MESSAGE_BUILDER_H
     7
     8
     9#include <Message.h>
     10#include <ObjectList.h>
     11#include <String.h>
     12
     13namespace BPrivate {
     14
     15class BMessageBuilder {
     16public:
     17                                BMessageBuilder(BMessage& message);
     18
     19            status_t            PushObject(const char* name);
     20            status_t            PushObject(uint32 name);
     21            status_t            PopObject();
     22           
     23            uint32              What();
     24            void                SetWhat(uint32 what);
     25           
     26            uint32              CountNames(type_code type = B_ANY_TYPE);
     27
     28            // Copied from Message.h
     29            status_t            AddString(const char* name, const char* string);
     30            status_t            AddString(const char* name,
     31                                    const BString& string);
     32            status_t            AddInt8(const char* name, int8 value);
     33            status_t            AddUInt8(const char* name, uint8 value);
     34            status_t            AddInt16(const char* name, int16 value);
     35            status_t            AddUInt16(const char* name, uint16 value);
     36            status_t            AddInt32(const char* name, int32 value);
     37            status_t            AddUInt32(const char* name, uint32 value);
     38            status_t            AddInt64(const char* name, int64 value);
     39            status_t            AddUInt64(const char* name, uint64 value);
     40            status_t            AddBool(const char* name, bool value);
     41            status_t            AddFloat(const char* name, float value);
     42            status_t            AddDouble(const char* name, double value);
     43            status_t            AddPointer(const char* name,
     44                                    const void* pointer);
     45
     46private:
     47            BObjectList<BMessage>   fStack;
     48            BObjectList<BString>    fNameStack;
     49            BMessage*           fCurrentMessage;
     50};
     51
     52} // namespace BPrivate
     53
     54#endif  // MESSAGE_BUILDER_H
  • src/kits/shared/Jamfile

    diff --git a/src/kits/shared/Jamfile b/src/kits/shared/Jamfile
    index 212d499..6867447 100644
    a b for architectureObject in [ MultiArchSubDirSetup ] {  
    3333            HashString.cpp
    3434            IconButton.cpp
    3535            IconView.cpp
     36            Json.cpp
    3637            Keymap.cpp
    3738            LongAndDragTrackingFilter.cpp
     39            MessageBuilder.cpp
    3840            NaturalCompare.cpp
    3941            PromptWindow.cpp
    4042            QueryFile.cpp
  • new file src/kits/shared/Json.cpp

    diff --git a/src/kits/shared/Json.cpp b/src/kits/shared/Json.cpp
    new file mode 100644
    index 0000000..100e216
    - +  
     1/*
     2 * Copyright 2014, Augustin Cavalier (waddlesplash)
     3 * Distributed under the terms of the MIT License.
     4 */
     5
     6
     7#include <Json.h>
     8
     9#include <stdio.h>
     10#include <stdlib.h>
     11
     12#include <MessageBuilder.h>
     13#include <UnicodeChar.h>
     14
     15
     16// #pragma mark - Public methods
     17
     18namespace BPrivate {
     19
     20status_t
     21BJson::Parse(BMessage& message, const char* JSON)
     22{
     23    BString temp(JSON);
     24    return Parse(message, temp);
     25}
     26
     27
     28status_t
     29BJson::Parse(BMessage& message, BString& JSON)
     30{
     31    BMessageBuilder builder(message);
     32    int32 pos = 0;
     33    int32 length = JSON.Length();
     34   
     35    /* Locals used by the parser. */
     36    // Keeps track of the hierarchy (e.g. "{[{{") that has
     37    // been read in. Allows the parser to verify that openbraces
     38    // match up to closebraces and so on and so forth.
     39    BString hierarchy("");
     40    // Stores the key that was just read by the string parser,
     41    // in the case that we are parsing a map.
     42    BString key("");
     43   
     44    while (pos < length) {
     45        switch (JSON[pos]) {
     46        case '{':
     47            hierarchy += "{";
     48           
     49            if (hierarchy != "{") {
     50                if (builder.What() == JSON_TYPE_ARRAY)
     51                    builder.PushObject(builder.CountNames());
     52                else {
     53                    builder.PushObject(key.String());
     54                    key = "";
     55                }
     56            }
     57
     58            builder.SetWhat(JSON_TYPE_MAP);
     59            break;
     60
     61        case '}':
     62            if (hierarchy.EndsWith("{") && (hierarchy.Length() != 1)) {
     63                hierarchy.Truncate(hierarchy.Length() - 1);
     64                builder.PopObject();
     65            } else if (hierarchy.Length() == 1)
     66                return B_OK; // End of the JSON data
     67            else
     68                return B_BAD_DATA; // Unmatched closebrace
     69
     70            break;
     71
     72        case '[':
     73            hierarchy += "[";
     74           
     75            if (builder.What() == JSON_TYPE_ARRAY)
     76                builder.PushObject(builder.CountNames());
     77            else {
     78                builder.PushObject(key.String());
     79                key = "";
     80            }
     81
     82            builder.SetWhat(JSON_TYPE_ARRAY);
     83            break;
     84
     85        case ']':
     86            if (hierarchy.EndsWith("[")) {
     87                hierarchy.Truncate(hierarchy.Length() - 1);
     88                builder.PopObject();
     89            } else
     90                return B_BAD_DATA; // Unmatched closebracket
     91
     92            break;
     93
     94        case 't':
     95        {
     96            if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0)
     97                return B_BAD_DATA;
     98                // 'true' cannot be a key, it can only be a value
     99           
     100            if (_Parser_ParseConstant(JSON, pos, "true")) {
     101                if (builder.What() == JSON_TYPE_ARRAY)
     102                    key.SetToFormat("%zu", builder.CountNames());
     103                builder.AddBool(key.String(), true);
     104                key = "";
     105            } else
     106                return B_BAD_DATA;
     107                // "t" out in the middle of nowhere!?
     108
     109            break;
     110        }
     111
     112        case 'f':
     113        {
     114            if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0)
     115                return B_BAD_DATA;
     116                // 'false' cannot be a key, it can only be a value
     117           
     118            if (_Parser_ParseConstant(JSON, pos, "false")) {
     119                if (builder.What() == JSON_TYPE_ARRAY)
     120                    key.SetToFormat("%zu", builder.CountNames());
     121                builder.AddBool(key.String(), false);
     122                key = "";
     123            } else
     124                return B_BAD_DATA;
     125                // "f" out in the middle of nowhere!?
     126
     127            break;
     128        }
     129
     130        case 'n':
     131        {
     132            if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0)
     133                return B_BAD_DATA;
     134                // 'null' cannot be a key, it can only be a value
     135           
     136            if (_Parser_ParseConstant(JSON, pos, "null")) {
     137                if (builder.What() == JSON_TYPE_ARRAY)
     138                    key.SetToFormat("%zu", builder.CountNames());
     139                builder.AddPointer(key.String(), (void*)NULL);
     140                key = "";
     141            } else
     142                return B_BAD_DATA;
     143                // "n" out in the middle of nowhere!?
     144
     145            break;
     146        }
     147
     148        case '"':
     149            if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0)
     150                key = _Parser_ParseString(JSON, pos);
     151            else if (builder.What() != JSON_TYPE_ARRAY && key.Length() > 0) {
     152                builder.AddString(key, _Parser_ParseString(JSON, pos));
     153                key = "";
     154            } else if (builder.What() == JSON_TYPE_ARRAY) {
     155                key << builder.CountNames();
     156                builder.AddString(key, _Parser_ParseString(JSON, pos));
     157                key = "";
     158            } else
     159                return B_BAD_DATA;
     160                // Pretty sure it's impossible to get here, but you never know
     161
     162            break;
     163
     164        case '+':
     165        case '-':
     166        case '0':
     167        case '1':
     168        case '2':
     169        case '3':
     170        case '4':
     171        case '5':
     172        case '6':
     173        case '7':
     174        case '8':
     175        case '9':
     176        {
     177            if (builder.What() != JSON_TYPE_ARRAY && key.Length() == 0)
     178                return B_BAD_DATA;
     179                // Numbers cannot be keys, they can only be values
     180           
     181            if (builder.What() == JSON_TYPE_ARRAY)
     182                key << builder.CountNames();
     183
     184            double number = _Parser_ParseNumber(JSON, pos);
     185            builder.AddDouble(key.String(), number);
     186
     187            key = "";
     188            break;
     189        }
     190
     191        case ':':
     192        case ',':
     193        default:
     194            // No need to do anything here.
     195            break;
     196        }
     197        pos++;
     198    }
     199
     200    // Reached the end of the BString without reaching the end of the document
     201    return B_BAD_DATA;
     202}
     203
     204
     205// #pragma mark - Private methods
     206
     207
     208BString
     209BJson::_Parser_ParseString(BString& JSON, int32& pos)
     210{
     211    if (JSON[pos] != '"') // Verify we're at the start of a string.
     212        return BString("");
     213    pos++;
     214   
     215    BString str;
     216    while (JSON[pos] != '"') {
     217        if (JSON[pos] == '\\') {
     218            pos++;
     219            switch (JSON[pos]) {
     220            case 'b':
     221                str += "\b";
     222                break;
     223
     224            case 'f':
     225                str += "\f";
     226                break;
     227
     228            case 'n':
     229                str += "\n";
     230                break;
     231
     232            case 'r':
     233                str += "\r";
     234                break;
     235
     236            case 't':
     237                str += "\t";
     238                break;
     239
     240            case 'u': // 4-byte hexadecimal Unicode char (e.g. "\uffff")
     241            {
     242                uint intValue;
     243                BString substr;
     244                JSON.CopyInto(substr, pos + 1, 4);
     245                if (sscanf(substr.String(), "%4x", &intValue) != 1)
     246                    return str;
     247                    // We probably hit the end of the string.
     248                    // This probably should be counted as an error,
     249                    // but for now let's soft-fail instead of hard-fail.
     250
     251                char character[20];
     252                char* ptr = character;
     253                BUnicodeChar::ToUTF8(intValue, &ptr);
     254                str.AppendChars(character, 1);
     255                pos += 4;
     256                break;
     257            }
     258
     259            default:
     260                str += JSON[pos];
     261                break;
     262            }
     263        } else
     264            str += JSON[pos];
     265        pos++;
     266    }
     267
     268    return str;
     269}
     270
     271
     272double
     273BJson::_Parser_ParseNumber(BString& JSON, int32& pos)
     274{
     275    BString value;
     276
     277    while (true) {
     278        switch (JSON[pos]) {
     279        case '+':
     280        case '-':
     281        case 'e':
     282        case 'E':
     283        case '0':
     284        case '1':
     285        case '2':
     286        case '3':
     287        case '4':
     288        case '5':
     289        case '6':
     290        case '7':
     291        case '8':
     292        case '9':
     293        case '.':
     294            value += JSON[pos];
     295            pos++;
     296            continue;
     297
     298        default:
     299            // We've reached the end of the number, so decrement the
     300            // "pos" value so that the parser picks up on the next char.
     301            pos--;
     302            break;
     303        }
     304        break;
     305    }
     306   
     307    return strtod(value.String(), NULL);
     308}
     309
     310
     311bool
     312BJson::_Parser_ParseConstant(BString& JSON, int32& pos, const char* constant)
     313{
     314    BString value;
     315    JSON.CopyInto(value, pos, strlen(constant));
     316    if (value == constant) {
     317        pos += strlen(constant);
     318        return true;
     319    } else
     320        return false;
     321}
     322
     323
     324} // namespace BPrivate
     325
  • new file src/kits/shared/MessageBuilder.cpp

    diff --git a/src/kits/shared/MessageBuilder.cpp b/src/kits/shared/MessageBuilder.cpp
    new file mode 100644
    index 0000000..d364abe
    - +  
     1/*
     2 * Copyright 2014, Augustin Cavalier (waddlesplash)
     3 * Distributed under the terms of the MIT License.
     4 */
     5
     6
     7#include <MessageBuilder.h>
     8
     9#include <String.h>
     10
     11
     12namespace BPrivate {
     13
     14// #pragma mark - BMessageBuilder
     15
     16
     17BMessageBuilder::BMessageBuilder(BMessage& message)
     18    :
     19    fCurrentMessage(&message),
     20    fNameStack(20, true)
     21{
     22}
     23
     24
     25/*! Creates a new BMessage, makes it a child of the
     26    current one with "name", and then pushes the current
     27    Message onto the stack and makes the new Message the
     28    current one.
     29*/
     30status_t
     31BMessageBuilder::PushObject(const char* name)
     32{
     33    BMessage* newMessage = new BMessage;
     34    if (newMessage == NULL)
     35        return B_NO_MEMORY;
     36   
     37    if (!fNameStack.AddItem(new BString(name)))
     38        return B_ERROR;
     39    if (!fStack.AddItem(fCurrentMessage))
     40        return B_ERROR;
     41
     42    fCurrentMessage = newMessage;
     43    return B_OK;
     44}
     45
     46
     47/*! Convenience function that converts "name"
     48    to a string and calls PushObject(const char*)
     49    with it.
     50*/
     51status_t
     52BMessageBuilder::PushObject(uint32 name)
     53{
     54    BString nameString;
     55    nameString.SetToFormat("%zu", name);
     56    return PushObject(nameString.String());
     57}
     58
     59
     60/*! Pops the last BMessage off the stack and makes it
     61    the current one.
     62*/
     63status_t
     64BMessageBuilder::PopObject()
     65{
     66    if (fStack.CountItems() < 1)
     67        return B_ERROR;
     68
     69    BMessage* previousMessage = fStack.LastItem();
     70    previousMessage->AddMessage(fNameStack.LastItem()->String(),
     71                                fCurrentMessage);
     72    fCurrentMessage = previousMessage;
     73
     74    fStack.RemoveItemAt(fStack.CountItems() - 1);
     75    fNameStack.RemoveItemAt(fNameStack.CountItems() - 1);
     76    return B_OK;
     77}
     78
     79
     80/*! Gets the "what" of the current message.
     81*/
     82uint32
     83BMessageBuilder::What()
     84{
     85    return fCurrentMessage->what;
     86}
     87
     88
     89/*! Sets the "what" of the current message.
     90*/
     91void
     92BMessageBuilder::SetWhat(uint32 what)
     93{
     94    fCurrentMessage->what = what;
     95}
     96
     97
     98/*! Gets the value of CountNames() from the current message.
     99*/
     100uint32
     101BMessageBuilder::CountNames(type_code type)
     102{
     103    return fCurrentMessage->CountNames(type);
     104}
     105
     106
     107// #pragma mark - BMessageBuilder::Add (to fCurrentMessage)
     108
     109
     110status_t
     111BMessageBuilder::AddString(const char* name, const char* string)
     112{
     113    return fCurrentMessage->AddString(name, string);
     114}
     115
     116
     117status_t
     118BMessageBuilder::AddString(const char* name, const BString& string)
     119{
     120    return fCurrentMessage->AddString(name, string);
     121}
     122
     123
     124status_t
     125BMessageBuilder::AddInt8(const char* name, int8 value)
     126{
     127    return fCurrentMessage->AddInt8(name, value);
     128}
     129
     130
     131status_t
     132BMessageBuilder::AddUInt8(const char* name, uint8 value)
     133{
     134    return fCurrentMessage->AddUInt8(name, value);
     135}
     136
     137
     138status_t
     139BMessageBuilder::AddInt16(const char* name, int16 value)
     140{
     141    return fCurrentMessage->AddInt16(name, value);
     142}
     143
     144
     145status_t
     146BMessageBuilder::AddUInt16(const char* name, uint16 value)
     147{
     148    return fCurrentMessage->AddUInt16(name, value);
     149}
     150
     151
     152status_t
     153BMessageBuilder::AddInt32(const char* name, int32 value)
     154{
     155    return fCurrentMessage->AddInt32(name, value);
     156}
     157
     158
     159status_t
     160BMessageBuilder::AddUInt32(const char* name, uint32 value)
     161{
     162    return fCurrentMessage->AddUInt32(name, value);
     163}
     164
     165
     166status_t
     167BMessageBuilder::AddInt64(const char* name, int64 value)
     168{
     169    return fCurrentMessage->AddInt64(name, value);
     170}
     171
     172
     173status_t
     174BMessageBuilder::AddUInt64(const char* name, uint64 value)
     175{
     176    return fCurrentMessage->AddUInt64(name, value);
     177}
     178
     179
     180status_t
     181BMessageBuilder::AddBool(const char* name, bool value)
     182{
     183    return fCurrentMessage->AddBool(name, value);
     184}
     185
     186
     187status_t
     188BMessageBuilder::AddFloat(const char* name, float value)
     189{
     190    return fCurrentMessage->AddFloat(name, value);
     191}
     192
     193
     194status_t
     195BMessageBuilder::AddDouble(const char* name, double value)
     196{
     197    return fCurrentMessage->AddDouble(name, value);
     198}
     199
     200
     201status_t
     202BMessageBuilder::AddPointer(const char* name, const void* pointer)
     203{
     204    return fCurrentMessage->AddPointer(name, pointer);
     205}
     206
     207
     208} // namespace BPrivate