Ticket #11104: 0001-libshared-Introduce-JSON-parser-and-MessageBuilder.patch
File 0001-libshared-Introduce-JSON-parser-and-MessageBuilder.patch, 15.1 KB (added by , 10 years ago) |
---|
-
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 11 namespace BPrivate { 12 13 class BJson { 14 public: 15 enum JsonObjectType { 16 JSON_TYPE_MAP = '_JTM', 17 JSON_TYPE_ARRAY = '_JTA' 18 }; 19 20 public: 21 static status_t Parse(BMessage& message, const char* JSON); 22 static status_t Parse(BMessage& message, BString& JSON); 23 24 private: 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 13 namespace BPrivate { 14 15 class BMessageBuilder { 16 public: 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 46 private: 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 ] { 33 33 HashString.cpp 34 34 IconButton.cpp 35 35 IconView.cpp 36 Json.cpp 36 37 Keymap.cpp 37 38 LongAndDragTrackingFilter.cpp 39 MessageBuilder.cpp 38 40 NaturalCompare.cpp 39 41 PromptWindow.cpp 40 42 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 18 namespace BPrivate { 19 20 status_t 21 BJson::Parse(BMessage& message, const char* JSON) 22 { 23 BString temp(JSON); 24 return Parse(message, temp); 25 } 26 27 28 status_t 29 BJson::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 208 BString 209 BJson::_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 272 double 273 BJson::_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 311 bool 312 BJson::_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 12 namespace BPrivate { 13 14 // #pragma mark - BMessageBuilder 15 16 17 BMessageBuilder::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 */ 30 status_t 31 BMessageBuilder::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 */ 51 status_t 52 BMessageBuilder::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 */ 63 status_t 64 BMessageBuilder::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 */ 82 uint32 83 BMessageBuilder::What() 84 { 85 return fCurrentMessage->what; 86 } 87 88 89 /*! Sets the "what" of the current message. 90 */ 91 void 92 BMessageBuilder::SetWhat(uint32 what) 93 { 94 fCurrentMessage->what = what; 95 } 96 97 98 /*! Gets the value of CountNames() from the current message. 99 */ 100 uint32 101 BMessageBuilder::CountNames(type_code type) 102 { 103 return fCurrentMessage->CountNames(type); 104 } 105 106 107 // #pragma mark - BMessageBuilder::Add (to fCurrentMessage) 108 109 110 status_t 111 BMessageBuilder::AddString(const char* name, const char* string) 112 { 113 return fCurrentMessage->AddString(name, string); 114 } 115 116 117 status_t 118 BMessageBuilder::AddString(const char* name, const BString& string) 119 { 120 return fCurrentMessage->AddString(name, string); 121 } 122 123 124 status_t 125 BMessageBuilder::AddInt8(const char* name, int8 value) 126 { 127 return fCurrentMessage->AddInt8(name, value); 128 } 129 130 131 status_t 132 BMessageBuilder::AddUInt8(const char* name, uint8 value) 133 { 134 return fCurrentMessage->AddUInt8(name, value); 135 } 136 137 138 status_t 139 BMessageBuilder::AddInt16(const char* name, int16 value) 140 { 141 return fCurrentMessage->AddInt16(name, value); 142 } 143 144 145 status_t 146 BMessageBuilder::AddUInt16(const char* name, uint16 value) 147 { 148 return fCurrentMessage->AddUInt16(name, value); 149 } 150 151 152 status_t 153 BMessageBuilder::AddInt32(const char* name, int32 value) 154 { 155 return fCurrentMessage->AddInt32(name, value); 156 } 157 158 159 status_t 160 BMessageBuilder::AddUInt32(const char* name, uint32 value) 161 { 162 return fCurrentMessage->AddUInt32(name, value); 163 } 164 165 166 status_t 167 BMessageBuilder::AddInt64(const char* name, int64 value) 168 { 169 return fCurrentMessage->AddInt64(name, value); 170 } 171 172 173 status_t 174 BMessageBuilder::AddUInt64(const char* name, uint64 value) 175 { 176 return fCurrentMessage->AddUInt64(name, value); 177 } 178 179 180 status_t 181 BMessageBuilder::AddBool(const char* name, bool value) 182 { 183 return fCurrentMessage->AddBool(name, value); 184 } 185 186 187 status_t 188 BMessageBuilder::AddFloat(const char* name, float value) 189 { 190 return fCurrentMessage->AddFloat(name, value); 191 } 192 193 194 status_t 195 BMessageBuilder::AddDouble(const char* name, double value) 196 { 197 return fCurrentMessage->AddDouble(name, value); 198 } 199 200 201 status_t 202 BMessageBuilder::AddPointer(const char* name, const void* pointer) 203 { 204 return fCurrentMessage->AddPointer(name, pointer); 205 } 206 207 208 } // namespace BPrivate