1/* 2 * Copyright 2017-2020, Andrew Lindesay <apl@lindesay.co.nz>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 7#include "StandardMetaDataJsonEventListener.h" 8 9#include "Logger.h" 10 11 12#define KEY_CREATE_TIMESTAMP "createTimestamp" 13#define KEY_DATA_MODIFIED_TIMESTAMP "dataModifiedTimestamp" 14 15 16class SmdStackedEventListener : public BJsonEventListener { 17public: 18 SmdStackedEventListener( 19 StandardMetaDataJsonEventListener* 20 mainListener, 21 SmdStackedEventListener* parent); 22 ~SmdStackedEventListener(); 23 24 void HandleError(status_t status, int32 line, 25 const char* message); 26 void Complete(); 27 28 status_t ErrorStatus(); 29 30 SmdStackedEventListener* 31 Parent(); 32 StandardMetaData* 33 MetaData(); 34 35protected: 36 void SetStackedListenerMainListener( 37 SmdStackedEventListener* stackedListener); 38 39 StandardMetaDataJsonEventListener* 40 fMainListener; 41 SmdStackedEventListener* 42 fParent; 43}; 44 45 46class SmdStackedArrayEventListener : public SmdStackedEventListener { 47public: 48 SmdStackedArrayEventListener( 49 StandardMetaDataJsonEventListener* 50 mainListener, 51 SmdStackedEventListener* parent); 52 ~SmdStackedArrayEventListener(); 53 54 bool Handle(const BJsonEvent& event); 55 56}; 57 58 59class SmdStackedObjectMessageEventListener : public SmdStackedEventListener { 60public: 61 SmdStackedObjectMessageEventListener( 62 BStringList* jsonPathObjectNames, 63 StandardMetaDataJsonEventListener* 64 mainListener, 65 SmdStackedEventListener* parent); 66 ~SmdStackedObjectMessageEventListener(); 67 68 bool Handle(const BJsonEvent& event); 69 70private: 71 BStringList* fJsonPathObjectNames; 72 BString fNextItemName; 73}; 74 75 76// #pragma mark - SmdStackedEventListener 77 78SmdStackedEventListener::SmdStackedEventListener( 79 StandardMetaDataJsonEventListener* mainListener, 80 SmdStackedEventListener* parent) 81{ 82 fMainListener = mainListener; 83 fParent = parent; 84} 85 86 87SmdStackedEventListener::~SmdStackedEventListener() 88{ 89} 90 91 92void 93SmdStackedEventListener::HandleError(status_t status, int32 line, 94 const char* message) 95{ 96 fMainListener->HandleError(status, line, message); 97} 98 99 100void 101SmdStackedEventListener::Complete() 102{ 103 fMainListener->Complete(); 104} 105 106 107status_t 108SmdStackedEventListener::ErrorStatus() 109{ 110 return fMainListener->ErrorStatus(); 111} 112 113 114StandardMetaData* 115SmdStackedEventListener::MetaData() 116{ 117 return fMainListener->MetaData(); 118} 119 120 121SmdStackedEventListener* 122SmdStackedEventListener::Parent() 123{ 124 return fParent; 125} 126 127 128void 129SmdStackedEventListener::SetStackedListenerMainListener( 130 SmdStackedEventListener* stackedListener) 131{ 132 fMainListener->SetStackedListener(stackedListener); 133} 134 135 136// #pragma mark - SmdStackedArrayEventListener 137 138 139SmdStackedArrayEventListener::SmdStackedArrayEventListener( 140 StandardMetaDataJsonEventListener* mainListener, 141 SmdStackedEventListener* parent) 142 : 143 SmdStackedEventListener(mainListener, parent) 144{ 145} 146 147 148SmdStackedArrayEventListener::~SmdStackedArrayEventListener() 149{ 150} 151 152 153bool 154SmdStackedArrayEventListener::Handle(const BJsonEvent& event) 155{ 156 if (ErrorStatus() != B_OK) 157 return false; 158 159 switch (event.EventType()) { 160 161 case B_JSON_NUMBER: 162 case B_JSON_STRING: 163 case B_JSON_TRUE: 164 case B_JSON_FALSE: 165 case B_JSON_NULL: 166 // ignore these atomic types in an array. 167 break; 168 169 case B_JSON_OBJECT_START: 170 SetStackedListenerMainListener( 171 new SmdStackedObjectMessageEventListener(NULL, fMainListener, 172 this)); 173 break; 174 175 case B_JSON_ARRAY_START: 176 SetStackedListenerMainListener(new SmdStackedArrayEventListener( 177 fMainListener, this)); 178 break; 179 180 case B_JSON_ARRAY_END: 181 SetStackedListenerMainListener(fParent); 182 delete this; 183 break; 184 185 case B_JSON_OBJECT_END: 186 case B_JSON_OBJECT_NAME: 187 HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, 188 "illegal state when processing events in an array"); 189 return false; 190 } 191 192 return true; 193} 194 195 196// #pragma mark - SmdStackedObjectMessageEventListener 197 198SmdStackedObjectMessageEventListener::SmdStackedObjectMessageEventListener( 199 BStringList* jsonPathObjectNames, 200 StandardMetaDataJsonEventListener* mainListener, 201 SmdStackedEventListener* parent) 202 : 203 SmdStackedEventListener(mainListener, parent) 204{ 205 fJsonPathObjectNames = jsonPathObjectNames; 206} 207 208 209SmdStackedObjectMessageEventListener::~SmdStackedObjectMessageEventListener() 210{ 211 if (fJsonPathObjectNames != NULL) 212 delete fJsonPathObjectNames; 213} 214 215 216bool 217SmdStackedObjectMessageEventListener::Handle(const BJsonEvent& event) 218{ 219 if (ErrorStatus() != B_OK) 220 return false; 221 222 switch (event.EventType()) { 223 224 case B_JSON_OBJECT_NAME: 225 fNextItemName = event.Content(); 226 break; 227 228 case B_JSON_NUMBER: 229 if (fJsonPathObjectNames != NULL 230 && fJsonPathObjectNames->IsEmpty()) { 231 232 if (fNextItemName == KEY_CREATE_TIMESTAMP) { 233 MetaData()->SetCreateTimestamp( 234 event.ContentInteger()); 235 } 236 237 if (fNextItemName == KEY_DATA_MODIFIED_TIMESTAMP) { 238 MetaData()->SetDataModifiedTimestamp( 239 event.ContentInteger()); 240 } 241 } 242 break; 243 244 case B_JSON_STRING: 245 case B_JSON_TRUE: 246 case B_JSON_FALSE: 247 case B_JSON_NULL: 248 // ignore these atomic types as they are not required to fill the 249 // data structure. 250 break; 251 252 case B_JSON_OBJECT_START: 253 { 254 BStringList* nextJsonPathObjectNames = NULL; 255 256 // if this next object is on the path then remove it from the 257 // path and carry on down. If it's not on the path then just 258 // drop the path from the next object. 259 260 if (fJsonPathObjectNames != NULL 261 && !fJsonPathObjectNames->IsEmpty() 262 && fNextItemName == fJsonPathObjectNames->StringAt(0)) { 263 nextJsonPathObjectNames = new BStringList(*fJsonPathObjectNames); 264 nextJsonPathObjectNames->Remove(0); 265 } 266 267 SetStackedListenerMainListener( 268 new SmdStackedObjectMessageEventListener(nextJsonPathObjectNames, 269 fMainListener, this)); 270 break; 271 } 272 273 case B_JSON_ARRAY_START: 274 SetStackedListenerMainListener(new SmdStackedArrayEventListener( 275 fMainListener, this)); 276 break; 277 278 case B_JSON_OBJECT_END: 279 SetStackedListenerMainListener(fParent); 280 delete this; 281 break; 282 283 case B_JSON_ARRAY_END: 284 HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE, 285 "illegal state when processing events in an array"); 286 return false; 287 } 288 289 return true; 290} 291 292 293// #pragma mark - StandardMetaDataJsonEventListener 294 295 296StandardMetaDataJsonEventListener::StandardMetaDataJsonEventListener( 297 const BString& jsonPath, 298 StandardMetaData& metaData) 299{ 300 fMetaData = &metaData; 301 SetJsonPath(jsonPath); 302 fErrorStatus = B_OK; 303 fStackedListener = NULL; 304} 305 306 307StandardMetaDataJsonEventListener::~StandardMetaDataJsonEventListener() 308{ 309 if (fStackedListener != NULL) 310 delete fStackedListener; 311} 312 313 314void 315StandardMetaDataJsonEventListener::SetJsonPath(const BString& jsonPath) 316{ 317 jsonPath.Split(".", true, fJsonPathObjectNames); 318 319 if (fJsonPathObjectNames.IsEmpty()) { 320 HandleError(B_BAD_VALUE, JSON_EVENT_LISTENER_ANY_LINE, 321 "json path required"); 322 } else { 323 if (fJsonPathObjectNames.First() != "$") { 324 HandleError(B_BAD_VALUE, JSON_EVENT_LISTENER_ANY_LINE, 325 "illegal json path; should start with '$"); 326 } 327 } 328} 329 330 331void 332StandardMetaDataJsonEventListener::SetStackedListener( 333 SmdStackedEventListener *listener) 334{ 335 fStackedListener = listener; 336} 337 338 339bool 340StandardMetaDataJsonEventListener::Handle(const BJsonEvent& event) 341{ 342 if (fErrorStatus != B_OK) 343 return false; 344 345 // best to exit early if the parsing has obtained all of the required 346 // data already. 347 348 if (fMetaData->IsPopulated()) 349 return false; 350 351 if (fStackedListener != NULL) 352 return fStackedListener->Handle(event); 353 354 // the first thing that comes in must be an object container. It is 355 // bad data if it does not start with an object container. 356 357 switch (event.EventType()) { 358 359 case B_JSON_OBJECT_START: 360 { 361 BStringList* jsonPathObjectNames = new BStringList( 362 fJsonPathObjectNames); 363 jsonPathObjectNames->Remove(0); 364 365 SetStackedListener( 366 new SmdStackedObjectMessageEventListener( 367 jsonPathObjectNames, this, NULL) 368 ); 369 } 370 break; 371 372 default: 373 HandleError(B_BAD_DATA, JSON_EVENT_LISTENER_ANY_LINE, 374 "the top level element must be an object"); 375 return false; 376 377 } 378 379 return true; 380} 381 382 383void 384StandardMetaDataJsonEventListener::HandleError(status_t status, int32 line, 385 const char* message) 386{ 387 HDERROR("an error has arisen processing the standard " 388 "meta data; %s", message); 389 fErrorStatus = status; 390} 391 392 393void 394StandardMetaDataJsonEventListener::Complete() 395{ 396} 397 398 399status_t 400StandardMetaDataJsonEventListener::ErrorStatus() 401{ 402 return fErrorStatus; 403} 404 405 406StandardMetaData* 407StandardMetaDataJsonEventListener::MetaData() 408{ 409 return fMetaData; 410}