HaikuDepot : Streaming Icon Meta-Data Parser
More background work for later performance improvements. This change generalizes the parsing of meta-data from JSON streams as similarly structured meta-data is anticipated to be carried in other payloads. Unit tests have also been implemented to provide coverage on this new functionality.
This commit is contained in:
parent
96251548d1
commit
a1c3daa638
@ -1,6 +1,6 @@
|
||||
SubDir HAIKU_TOP src apps haikudepot ;
|
||||
|
||||
UsePrivateHeaders interface shared package support net ;
|
||||
UsePrivateHeaders interface shared storage package support net ;
|
||||
|
||||
# source directories
|
||||
local sourceDirs =
|
||||
@ -79,7 +79,8 @@ Application HaikuDepot :
|
||||
ServerSettings.cpp
|
||||
WebAppInterface.cpp
|
||||
ServerIconExportUpdateProcess.cpp
|
||||
IconMetaData.cpp
|
||||
StandardMetaDataJsonEventListener.cpp
|
||||
StandardMetaData.cpp
|
||||
|
||||
# tar
|
||||
TarArchiveHeader.cpp
|
||||
@ -123,4 +124,4 @@ Application TextDocumentTest :
|
||||
$(textDocumentSources)
|
||||
|
||||
: be translation shared [ TargetLibsupc++ ]
|
||||
;
|
||||
;
|
@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "IconMetaData.h"
|
||||
|
||||
|
||||
BDateTime
|
||||
IconMetaData::_CreateDateTime(uint64_t millisSinceEpoc)
|
||||
{
|
||||
time_t secondsSinceEpoc = (millisSinceEpoc / 1000);
|
||||
BDateTime result;
|
||||
result.SetTime_t(secondsSinceEpoc);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
uint64_t
|
||||
IconMetaData::GetCreateTimestamp()
|
||||
{
|
||||
return fCreateTimestamp;
|
||||
}
|
||||
|
||||
|
||||
BDateTime
|
||||
IconMetaData::GetCreateTimestampAsDateTime()
|
||||
{
|
||||
return _CreateDateTime(GetCreateTimestamp());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
IconMetaData::SetCreateTimestamp(uint64_t value)
|
||||
{
|
||||
fCreateTimestamp = value;
|
||||
}
|
||||
|
||||
|
||||
uint64_t
|
||||
IconMetaData::GetDataModifiedTimestamp()
|
||||
{
|
||||
return fDataModifiedTimestamp;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
IconMetaData::SetDataModifiedTimestamp(uint64_t value)
|
||||
{
|
||||
fDataModifiedTimestamp = value;
|
||||
}
|
||||
|
||||
BDateTime
|
||||
IconMetaData::GetDataModifiedTimestampAsDateTime()
|
||||
{
|
||||
return _CreateDateTime(GetDataModifiedTimestamp());
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <time.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <FileIO.h>
|
||||
#include <HttpRequest.h>
|
||||
#include <HttpTime.h>
|
||||
#include <Json.h>
|
||||
@ -19,6 +20,7 @@
|
||||
#include <support/ZlibCompressionAlgorithm.h>
|
||||
|
||||
#include "ServerSettings.h"
|
||||
#include "StandardMetaDataJsonEventListener.h"
|
||||
#include "StorageUtils.h"
|
||||
#include "TarArchiveService.h"
|
||||
#include "ToFileUrlProtocolListener.h"
|
||||
@ -80,7 +82,7 @@ ServerIconExportUpdateProcess::Run()
|
||||
if (result == B_OK) {
|
||||
if (0 != remove(tarGzFilePath.Path())) {
|
||||
fprintf(stdout, "unable to delete the temporary tgz path; "
|
||||
"%s", tarGzFilePath.Path());
|
||||
"%s\n", tarGzFilePath.Path());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,7 +110,7 @@ ServerIconExportUpdateProcess::_IfModifiedSinceHeaderValue(BString& headerValue,
|
||||
return B_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
IconMetaData iconMetaData;
|
||||
StandardMetaData iconMetaData;
|
||||
status_t result = _PopulateIconMetaData(iconMetaData, iconMetaDataPath);
|
||||
|
||||
if (result == B_OK) {
|
||||
@ -120,6 +122,9 @@ ServerIconExportUpdateProcess::_IfModifiedSinceHeaderValue(BString& headerValue,
|
||||
BPrivate::BHttpTime modifiedHttpTime(modifiedDateTime);
|
||||
headerValue.SetTo(modifiedHttpTime
|
||||
.ToString(BPrivate::B_HTTP_TIME_FORMAT_COOKIE));
|
||||
} else {
|
||||
fprintf(stderr, "unable to parse the icon meta-data date and time -"
|
||||
" cannot set the 'If-Modified-Since' header\n");
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -226,53 +231,33 @@ ServerIconExportUpdateProcess::_Download(BPath& tarGzFilePath, const BUrl& url,
|
||||
|
||||
|
||||
status_t
|
||||
ServerIconExportUpdateProcess::_PopulateIconMetaData(IconMetaData& iconMetaData,
|
||||
BMessage& message) const
|
||||
ServerIconExportUpdateProcess::_PopulateIconMetaData(
|
||||
StandardMetaData& iconMetaData, BPath& path) const
|
||||
{
|
||||
status_t result = B_OK;
|
||||
double value; // numeric resolution issue?
|
||||
FILE *file = fopen(path.Path(), "rb");
|
||||
|
||||
if (result == B_OK)
|
||||
result = message.FindDouble("createTimestamp", &value);
|
||||
if (file == NULL) {
|
||||
fprintf(stderr, "unable to find the icon meta data file at [%s]\n",
|
||||
path.Path());
|
||||
return B_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (result == B_OK)
|
||||
iconMetaData.SetCreateTimestamp((uint64) value);
|
||||
BFileIO iconMetaDataFile(file, true); // takes ownership
|
||||
// the "$" here indicates that the data is at the top level.
|
||||
StandardMetaDataJsonEventListener listener("$", iconMetaData);
|
||||
BPrivate::BJson::Parse(&iconMetaDataFile, &listener);
|
||||
|
||||
if (result == B_OK)
|
||||
result = message.FindDouble("dataModifiedTimestamp", &value);
|
||||
status_t result = listener.ErrorStatus();
|
||||
|
||||
if (result == B_OK)
|
||||
iconMetaData.SetDataModifiedTimestamp((uint64) value);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ServerIconExportUpdateProcess::_PopulateIconMetaData(IconMetaData& iconMetaData,
|
||||
BString& jsonString) const
|
||||
{
|
||||
BMessage infoMetaDataMessage;
|
||||
status_t result = BJson::Parse(jsonString, infoMetaDataMessage);
|
||||
|
||||
if (result == B_OK)
|
||||
return _PopulateIconMetaData(iconMetaData, infoMetaDataMessage);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ServerIconExportUpdateProcess::_PopulateIconMetaData(IconMetaData& iconMetaData,
|
||||
BPath& path) const
|
||||
{
|
||||
|
||||
BString infoMetaDataStr;
|
||||
status_t result = StorageUtils::AppendToString(path, infoMetaDataStr);
|
||||
|
||||
if (result == B_OK)
|
||||
return _PopulateIconMetaData(iconMetaData, infoMetaDataStr);
|
||||
|
||||
return result;
|
||||
if (!iconMetaData.IsPopulated()) {
|
||||
fprintf(stderr, "the icon meta data was read from [%s], but no values "
|
||||
"were extracted\n", path.Path());
|
||||
return B_BAD_DATA;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <String.h>
|
||||
#include <Url.h>
|
||||
|
||||
#include "IconMetaData.h"
|
||||
#include "StandardMetaData.h"
|
||||
|
||||
|
||||
class ServerIconExportUpdateProcess : public AbstractServerProcess {
|
||||
@ -37,14 +37,8 @@ private:
|
||||
BPath& iconMetaDataPath) const;
|
||||
|
||||
status_t _PopulateIconMetaData(
|
||||
IconMetaData& iconMetaData, BPath& path)
|
||||
StandardMetaData& iconMetaData, BPath& path)
|
||||
const;
|
||||
status_t _PopulateIconMetaData(
|
||||
IconMetaData& iconMetaData,
|
||||
BString& jsonString) const;
|
||||
status_t _PopulateIconMetaData(
|
||||
IconMetaData& iconMetaData,
|
||||
BMessage& message) const;
|
||||
|
||||
BString fBaseUrl;
|
||||
BPath fLocalStorageDirectoryPath;
|
||||
|
71
src/apps/haikudepot/server/StandardMetaData.cpp
Normal file
71
src/apps/haikudepot/server/StandardMetaData.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "StandardMetaData.h"
|
||||
|
||||
StandardMetaData::StandardMetaData()
|
||||
{
|
||||
fCreateTimestamp = 0;
|
||||
fDataModifiedTimestamp = 0;
|
||||
}
|
||||
|
||||
|
||||
BDateTime
|
||||
StandardMetaData::_CreateDateTime(uint64_t millisSinceEpoc)
|
||||
{
|
||||
time_t secondsSinceEpoc = (millisSinceEpoc / 1000);
|
||||
BDateTime result;
|
||||
result.SetTime_t(secondsSinceEpoc);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
uint64_t
|
||||
StandardMetaData::GetCreateTimestamp()
|
||||
{
|
||||
return fCreateTimestamp;
|
||||
}
|
||||
|
||||
|
||||
BDateTime
|
||||
StandardMetaData::GetCreateTimestampAsDateTime()
|
||||
{
|
||||
return _CreateDateTime(GetCreateTimestamp());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StandardMetaData::SetCreateTimestamp(uint64_t value)
|
||||
{
|
||||
fCreateTimestamp = value;
|
||||
}
|
||||
|
||||
|
||||
uint64_t
|
||||
StandardMetaData::GetDataModifiedTimestamp()
|
||||
{
|
||||
return fDataModifiedTimestamp;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StandardMetaData::SetDataModifiedTimestamp(uint64_t value)
|
||||
{
|
||||
fDataModifiedTimestamp = value;
|
||||
}
|
||||
|
||||
|
||||
BDateTime
|
||||
StandardMetaData::GetDataModifiedTimestampAsDateTime()
|
||||
{
|
||||
return _CreateDateTime(GetDataModifiedTimestamp());
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
StandardMetaData::IsPopulated()
|
||||
{
|
||||
return fCreateTimestamp != 0 && fDataModifiedTimestamp != 0;
|
||||
}
|
@ -2,10 +2,8 @@
|
||||
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ICON_META_DATA_H
|
||||
#define ICON_META_DATA_H
|
||||
#ifndef STANDARD_META_DATA_H
|
||||
#define STANDARD_META_DATA_H
|
||||
|
||||
#include <DateTime.h>
|
||||
#include <File.h>
|
||||
@ -14,14 +12,17 @@
|
||||
#include <String.h>
|
||||
|
||||
|
||||
/* This class models (some of) the meta-data that is bundled into the tar file
|
||||
* that is downloaded to the HaikuDepot client from the HaikuDepotServer
|
||||
* application server system. The file is included in the tar-ball data.
|
||||
/* This class models (some of) the meta-data that is bundled into data that is
|
||||
* relayed from the HaikuDepotServer application server down to the client.
|
||||
* This includes the tar-ball of icons as well as "bulk data" such as streams of
|
||||
* information about repositories and packages.
|
||||
*/
|
||||
|
||||
|
||||
class IconMetaData {
|
||||
class StandardMetaData {
|
||||
public:
|
||||
StandardMetaData();
|
||||
|
||||
uint64_t GetCreateTimestamp();
|
||||
BDateTime GetCreateTimestampAsDateTime();
|
||||
void SetCreateTimestamp(uint64_t value);
|
||||
@ -31,6 +32,7 @@ public:
|
||||
void SetDataModifiedTimestamp(
|
||||
uint64_t value);
|
||||
|
||||
bool IsPopulated();
|
||||
private:
|
||||
BDateTime _CreateDateTime(
|
||||
uint64_t millisSinceEpoc);
|
||||
@ -39,4 +41,4 @@ private:
|
||||
};
|
||||
|
||||
|
||||
#endif // ICON_META_DATA_H
|
||||
#endif // STANDARD_META_DATA_H
|
408
src/apps/haikudepot/server/StandardMetaDataJsonEventListener.cpp
Normal file
408
src/apps/haikudepot/server/StandardMetaDataJsonEventListener.cpp
Normal file
@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "StandardMetaDataJsonEventListener.h"
|
||||
|
||||
#include "stdio.h"
|
||||
|
||||
#define KEY_CREATE_TIMESTAMP "createTimestamp"
|
||||
#define KEY_DATA_MODIFIED_TIMESTAMP "dataModifiedTimestamp"
|
||||
|
||||
|
||||
class SmdStackedEventListener : public BJsonEventListener {
|
||||
public:
|
||||
SmdStackedEventListener(
|
||||
StandardMetaDataJsonEventListener*
|
||||
mainListener,
|
||||
SmdStackedEventListener* parent);
|
||||
~SmdStackedEventListener();
|
||||
|
||||
void HandleError(status_t status, int32 line,
|
||||
const char* message);
|
||||
void Complete();
|
||||
|
||||
status_t ErrorStatus();
|
||||
|
||||
SmdStackedEventListener*
|
||||
Parent();
|
||||
StandardMetaData*
|
||||
MetaData();
|
||||
|
||||
protected:
|
||||
void SetStackedListenerMainListener(
|
||||
SmdStackedEventListener* stackedListener);
|
||||
|
||||
StandardMetaDataJsonEventListener*
|
||||
fMainListener;
|
||||
SmdStackedEventListener*
|
||||
fParent;
|
||||
};
|
||||
|
||||
|
||||
class SmdStackedArrayEventListener : public SmdStackedEventListener {
|
||||
public:
|
||||
SmdStackedArrayEventListener(
|
||||
StandardMetaDataJsonEventListener*
|
||||
mainListener,
|
||||
SmdStackedEventListener* parent);
|
||||
~SmdStackedArrayEventListener();
|
||||
|
||||
bool Handle(const BJsonEvent& event);
|
||||
|
||||
};
|
||||
|
||||
|
||||
class SmdStackedObjectMessageEventListener : public SmdStackedEventListener {
|
||||
public:
|
||||
SmdStackedObjectMessageEventListener(
|
||||
BStringList* jsonPathObjectNames,
|
||||
StandardMetaDataJsonEventListener*
|
||||
mainListener,
|
||||
SmdStackedEventListener* parent);
|
||||
~SmdStackedObjectMessageEventListener();
|
||||
|
||||
bool Handle(const BJsonEvent& event);
|
||||
|
||||
private:
|
||||
BStringList* fJsonPathObjectNames;
|
||||
BString fNextItemName;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - SmdStackedEventListener
|
||||
|
||||
SmdStackedEventListener::SmdStackedEventListener(
|
||||
StandardMetaDataJsonEventListener* mainListener,
|
||||
SmdStackedEventListener* parent)
|
||||
{
|
||||
fMainListener = mainListener;
|
||||
fParent = parent;
|
||||
}
|
||||
|
||||
|
||||
SmdStackedEventListener::~SmdStackedEventListener()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SmdStackedEventListener::HandleError(status_t status, int32 line,
|
||||
const char* message)
|
||||
{
|
||||
fMainListener->HandleError(status, line, message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SmdStackedEventListener::Complete()
|
||||
{
|
||||
fMainListener->Complete();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SmdStackedEventListener::ErrorStatus()
|
||||
{
|
||||
return fMainListener->ErrorStatus();
|
||||
}
|
||||
|
||||
|
||||
StandardMetaData*
|
||||
SmdStackedEventListener::MetaData()
|
||||
{
|
||||
return fMainListener->MetaData();
|
||||
}
|
||||
|
||||
|
||||
SmdStackedEventListener*
|
||||
SmdStackedEventListener::Parent()
|
||||
{
|
||||
return fParent;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SmdStackedEventListener::SetStackedListenerMainListener(
|
||||
SmdStackedEventListener* stackedListener)
|
||||
{
|
||||
fMainListener->SetStackedListener(stackedListener);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - SmdStackedArrayEventListener
|
||||
|
||||
|
||||
SmdStackedArrayEventListener::SmdStackedArrayEventListener(
|
||||
StandardMetaDataJsonEventListener* mainListener,
|
||||
SmdStackedEventListener* parent)
|
||||
:
|
||||
SmdStackedEventListener(mainListener, parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
SmdStackedArrayEventListener::~SmdStackedArrayEventListener()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SmdStackedArrayEventListener::Handle(const BJsonEvent& event)
|
||||
{
|
||||
if (ErrorStatus() != B_OK)
|
||||
return false;
|
||||
|
||||
switch (event.EventType()) {
|
||||
|
||||
case B_JSON_NUMBER:
|
||||
case B_JSON_STRING:
|
||||
case B_JSON_TRUE:
|
||||
case B_JSON_FALSE:
|
||||
case B_JSON_NULL:
|
||||
// ignore these atomic types in an array.
|
||||
break;
|
||||
|
||||
case B_JSON_OBJECT_START:
|
||||
SetStackedListenerMainListener(
|
||||
new SmdStackedObjectMessageEventListener(NULL, fMainListener,
|
||||
this));
|
||||
break;
|
||||
|
||||
case B_JSON_ARRAY_START:
|
||||
SetStackedListenerMainListener(new SmdStackedArrayEventListener(
|
||||
fMainListener, this));
|
||||
break;
|
||||
|
||||
case B_JSON_ARRAY_END:
|
||||
SetStackedListenerMainListener(fParent);
|
||||
delete this;
|
||||
break;
|
||||
|
||||
case B_JSON_OBJECT_END:
|
||||
case B_JSON_OBJECT_NAME:
|
||||
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
|
||||
"illegal state when processing events in an array");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - SmdStackedObjectMessageEventListener
|
||||
|
||||
SmdStackedObjectMessageEventListener::SmdStackedObjectMessageEventListener(
|
||||
BStringList* jsonPathObjectNames,
|
||||
StandardMetaDataJsonEventListener* mainListener,
|
||||
SmdStackedEventListener* parent)
|
||||
:
|
||||
SmdStackedEventListener(mainListener, parent)
|
||||
{
|
||||
fJsonPathObjectNames = jsonPathObjectNames;
|
||||
}
|
||||
|
||||
|
||||
SmdStackedObjectMessageEventListener::~SmdStackedObjectMessageEventListener()
|
||||
{
|
||||
if (fJsonPathObjectNames != NULL)
|
||||
delete fJsonPathObjectNames;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SmdStackedObjectMessageEventListener::Handle(const BJsonEvent& event)
|
||||
{
|
||||
if (ErrorStatus() != B_OK)
|
||||
return false;
|
||||
|
||||
switch (event.EventType()) {
|
||||
|
||||
case B_JSON_OBJECT_NAME:
|
||||
fNextItemName = event.Content();
|
||||
break;
|
||||
|
||||
case B_JSON_NUMBER:
|
||||
if (fJsonPathObjectNames != NULL
|
||||
&& fJsonPathObjectNames->IsEmpty()) {
|
||||
|
||||
if (fNextItemName == KEY_CREATE_TIMESTAMP) {
|
||||
MetaData()->SetCreateTimestamp(
|
||||
event.ContentInteger());
|
||||
}
|
||||
|
||||
if (fNextItemName == KEY_DATA_MODIFIED_TIMESTAMP) {
|
||||
MetaData()->SetDataModifiedTimestamp(
|
||||
event.ContentInteger());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case B_JSON_STRING:
|
||||
case B_JSON_TRUE:
|
||||
case B_JSON_FALSE:
|
||||
case B_JSON_NULL:
|
||||
// ignore these atomic types as they are not required to fill the
|
||||
// data structure.
|
||||
break;
|
||||
|
||||
case B_JSON_OBJECT_START:
|
||||
{
|
||||
BStringList* nextJsonPathObjectNames = NULL;
|
||||
|
||||
// if this next object is on the path then remove it from the
|
||||
// path and carry on down. If it's not on the path then just
|
||||
// drop the path from the next object.
|
||||
|
||||
if (fJsonPathObjectNames != NULL
|
||||
&& !fJsonPathObjectNames->IsEmpty()
|
||||
&& fNextItemName == fJsonPathObjectNames->StringAt(0)) {
|
||||
nextJsonPathObjectNames = new BStringList(*fJsonPathObjectNames);
|
||||
nextJsonPathObjectNames->Remove(0);
|
||||
}
|
||||
|
||||
SetStackedListenerMainListener(
|
||||
new SmdStackedObjectMessageEventListener(nextJsonPathObjectNames,
|
||||
fMainListener, this));
|
||||
break;
|
||||
}
|
||||
|
||||
case B_JSON_ARRAY_START:
|
||||
SetStackedListenerMainListener(new SmdStackedArrayEventListener(
|
||||
fMainListener, this));
|
||||
break;
|
||||
|
||||
case B_JSON_OBJECT_END:
|
||||
SetStackedListenerMainListener(fParent);
|
||||
delete this;
|
||||
break;
|
||||
|
||||
case B_JSON_ARRAY_END:
|
||||
HandleError(B_NOT_ALLOWED, JSON_EVENT_LISTENER_ANY_LINE,
|
||||
"illegal state when processing events in an array");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - StandardMetaDataJsonEventListener
|
||||
|
||||
|
||||
StandardMetaDataJsonEventListener::StandardMetaDataJsonEventListener(
|
||||
const BString& jsonPath,
|
||||
StandardMetaData& metaData)
|
||||
{
|
||||
fMetaData = &metaData;
|
||||
SetJsonPath(jsonPath);
|
||||
fErrorStatus = B_OK;
|
||||
fStackedListener = NULL;
|
||||
}
|
||||
|
||||
|
||||
StandardMetaDataJsonEventListener::~StandardMetaDataJsonEventListener()
|
||||
{
|
||||
if (fStackedListener != NULL)
|
||||
delete fStackedListener;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StandardMetaDataJsonEventListener::SetJsonPath(const BString& jsonPath)
|
||||
{
|
||||
jsonPath.Split(".", true, fJsonPathObjectNames);
|
||||
|
||||
if (fJsonPathObjectNames.IsEmpty()) {
|
||||
HandleError(B_BAD_VALUE, JSON_EVENT_LISTENER_ANY_LINE,
|
||||
"json path required");
|
||||
} else {
|
||||
if (fJsonPathObjectNames.First() != "$") {
|
||||
HandleError(B_BAD_VALUE, JSON_EVENT_LISTENER_ANY_LINE,
|
||||
"illegal json path; should start with '$");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StandardMetaDataJsonEventListener::SetStackedListener(
|
||||
SmdStackedEventListener *listener)
|
||||
{
|
||||
fStackedListener = listener;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
StandardMetaDataJsonEventListener::Handle(const BJsonEvent& event)
|
||||
{
|
||||
if (fErrorStatus != B_OK)
|
||||
return false;
|
||||
|
||||
// best to exit early if the parsing has obtained all of the required
|
||||
// data already.
|
||||
|
||||
if (fMetaData->IsPopulated())
|
||||
return false;
|
||||
|
||||
if (fStackedListener != NULL)
|
||||
return fStackedListener->Handle(event);
|
||||
|
||||
// the first thing that comes in must be an object container. It is
|
||||
// bad data if it does not start with an object container.
|
||||
|
||||
switch (event.EventType()) {
|
||||
|
||||
case B_JSON_OBJECT_START:
|
||||
{
|
||||
BStringList* jsonPathObjectNames = new BStringList(
|
||||
fJsonPathObjectNames);
|
||||
jsonPathObjectNames->Remove(0);
|
||||
|
||||
SetStackedListener(
|
||||
new SmdStackedObjectMessageEventListener(
|
||||
jsonPathObjectNames, this, NULL)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
HandleError(B_BAD_DATA, JSON_EVENT_LISTENER_ANY_LINE,
|
||||
"the top level element must be an object");
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StandardMetaDataJsonEventListener::HandleError(status_t status, int32 line,
|
||||
const char* message)
|
||||
{
|
||||
fprintf(stderr, "an error has arisen processing the standard "
|
||||
"meta data; %s\n", message);
|
||||
fErrorStatus = status;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StandardMetaDataJsonEventListener::Complete()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
StandardMetaDataJsonEventListener::ErrorStatus()
|
||||
{
|
||||
return fErrorStatus;
|
||||
}
|
||||
|
||||
|
||||
StandardMetaData*
|
||||
StandardMetaDataJsonEventListener::MetaData()
|
||||
{
|
||||
return fMetaData;
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef STANDARD_META_DATA_JSON_EVENT_LISTENER_H
|
||||
#define STANDARD_META_DATA_JSON_EVENT_LISTENER_H
|
||||
|
||||
#include "StandardMetaData.h"
|
||||
|
||||
#include <JsonEventListener.h>
|
||||
#include <String.h>
|
||||
#include <StringList.h>
|
||||
|
||||
class SmdStackedEventListener;
|
||||
|
||||
class StandardMetaDataJsonEventListener : public BJsonEventListener {
|
||||
friend class SmdStackedEventListener;
|
||||
public:
|
||||
StandardMetaDataJsonEventListener(
|
||||
const BString& jsonPath,
|
||||
StandardMetaData& metaData);
|
||||
virtual ~StandardMetaDataJsonEventListener();
|
||||
|
||||
bool Handle(const BJsonEvent& event);
|
||||
void HandleError(status_t status, int32 line,
|
||||
const char* message);
|
||||
void Complete();
|
||||
status_t ErrorStatus();
|
||||
|
||||
protected:
|
||||
void SetStackedListener(SmdStackedEventListener *listener);
|
||||
|
||||
private:
|
||||
void SetJsonPath(const BString& jsonPath);
|
||||
StandardMetaData* MetaData();
|
||||
|
||||
BStringList fJsonPathObjectNames;
|
||||
StandardMetaData* fMetaData;
|
||||
status_t fErrorStatus;
|
||||
SmdStackedEventListener*
|
||||
fStackedListener;
|
||||
};
|
||||
|
||||
|
||||
#endif // STANDARD_META_DATA_JSON_EVENT_LISTENER_H
|
@ -387,7 +387,7 @@ BJson::ParseObject(JsonParseContext& jsonParseContext)
|
||||
} else {
|
||||
jsonParseContext.Listener()->HandleError(B_BAD_DATA,
|
||||
jsonParseContext.LineNumber(), "expected"
|
||||
"separator when parsing an object");
|
||||
" separator when parsing an object");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -446,7 +446,7 @@ BJson::ParseArray(JsonParseContext& jsonParseContext)
|
||||
} else {
|
||||
jsonParseContext.Listener()->HandleError(B_BAD_DATA,
|
||||
jsonParseContext.LineNumber(), "expected"
|
||||
"separator when parsing an array");
|
||||
" separator when parsing an array");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ SubDir HAIKU_TOP src tests apps ;
|
||||
|
||||
SubInclude HAIKU_TOP src tests apps delay_shutdown ;
|
||||
SubInclude HAIKU_TOP src tests apps fake_app_server ;
|
||||
SubInclude HAIKU_TOP src tests apps haikudepot ;
|
||||
SubInclude HAIKU_TOP src tests apps installer ;
|
||||
SubInclude HAIKU_TOP src tests apps miniterminal ;
|
||||
SubInclude HAIKU_TOP src tests apps partitioner ;
|
||||
|
20
src/tests/apps/haikudepot/HaikuDepotTestAddon.cpp
Normal file
20
src/tests/apps/haikudepot/HaikuDepotTestAddon.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2017, Andrew Lindesay, apl@lindesay.co.nz
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include <TestSuite.h>
|
||||
#include <TestSuiteAddon.h>
|
||||
|
||||
#include "StandardMetaDataJsonEventListenerTest.h"
|
||||
|
||||
|
||||
BTestSuite*
|
||||
getTestSuite()
|
||||
{
|
||||
BTestSuite* suite = new BTestSuite("HaikuDepot");
|
||||
|
||||
StandardMetaDataJsonEventListenerTest::AddTests(*suite);
|
||||
|
||||
return suite;
|
||||
}
|
21
src/tests/apps/haikudepot/Jamfile
Normal file
21
src/tests/apps/haikudepot/Jamfile
Normal file
@ -0,0 +1,21 @@
|
||||
SubDir HAIKU_TOP src tests apps haikudepot ;
|
||||
|
||||
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||
AddSubDirSupportedPlatforms libbe_test ;
|
||||
|
||||
SubDirHdrs [ FDirName $(HAIKU_TOP) src apps haikudepot server ] ;
|
||||
|
||||
UsePrivateHeaders shared ;
|
||||
|
||||
UnitTestLib haikudepottest.so :
|
||||
HaikuDepotTestAddon.cpp
|
||||
|
||||
StandardMetaData.cpp
|
||||
StandardMetaDataJsonEventListener.cpp
|
||||
StandardMetaDataJsonEventListenerTest.cpp
|
||||
|
||||
: be shared bnetapi [ TargetLibstdc++ ] [ TargetLibsupc++ ]
|
||||
;
|
||||
|
||||
SEARCH on [ FGristFiles StandardMetaData.cpp StandardMetaDataJsonEventListener.cpp ]
|
||||
= [ FDirName $(HAIKU_TOP) src apps haikudepot server ] ;
|
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "StandardMetaData.h"
|
||||
#include "StandardMetaDataJsonEventListener.h"
|
||||
#include "StandardMetaDataJsonEventListenerTest.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <Json.h>
|
||||
|
||||
#include <cppunit/TestCaller.h>
|
||||
#include <cppunit/TestSuite.h>
|
||||
|
||||
|
||||
#define INPUT_TOP_LEVEL "{\"createTimestamp\":123456," \
|
||||
"\"dataModifiedTimestamp\":789012}"
|
||||
|
||||
// This example is from the HDS server and comes back as part of the icons
|
||||
// bundle; real-world example.
|
||||
|
||||
#define INPUT_ICON "{\"createTimestamp\":1501456139480," \
|
||||
"\"createTimestampIso\":\"2017-07-30 23:08:59\"," \
|
||||
"\"dataModifiedTimestamp\":1500536391000," \
|
||||
"\"dataModifiedTimestampIso\":\"2017-07-20 07:39:51\"," \
|
||||
"\"agent\":\"hds\"," \
|
||||
"\"agentVersion\":\"1.0.86\"" \
|
||||
"}"
|
||||
|
||||
#define INPUT_THIRD_LEVEL "{" \
|
||||
"\"createTimestamp\":99999," \
|
||||
"\"gonk\":{\"zink\":[ 123, { \"a\":\"b\" } ]}\n," \
|
||||
"\"rink\":{" \
|
||||
"\"tonk\":{" \
|
||||
"\"createTimestamp\":665544," \
|
||||
"\"dataModifiedTimestamp\":554433" \
|
||||
"}" \
|
||||
"}," \
|
||||
"\"cake\": { \"type\": \"bananas\" }," \
|
||||
"\"createTimestamp\":99999," \
|
||||
"}"
|
||||
|
||||
#define INPUT_BROKEN "{\"broken\",,"
|
||||
|
||||
|
||||
StandardMetaDataJsonEventListenerTest::StandardMetaDataJsonEventListenerTest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
StandardMetaDataJsonEventListenerTest::~StandardMetaDataJsonEventListenerTest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StandardMetaDataJsonEventListenerTest::TestGeneric(
|
||||
const char* input, const char* jsonPath, status_t expectedStatus,
|
||||
uint64_t expectedCreateTimestamp, uint64_t expectedDataModifiedTimestamp)
|
||||
{
|
||||
StandardMetaData metaData;
|
||||
StandardMetaDataJsonEventListener listener(jsonPath, metaData);
|
||||
|
||||
BDataIO* inputData = new BMemoryIO(input, strlen(input));
|
||||
ObjectDeleter<BDataIO> inputDataDeleter(inputData);
|
||||
BMallocIO* outputData = new BMallocIO();
|
||||
ObjectDeleter<BMallocIO> outputDataDeleter(outputData);
|
||||
|
||||
// ----------------------
|
||||
BPrivate::BJson::Parse(inputData, &listener);
|
||||
// ----------------------
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(expectedStatus, listener.ErrorStatus());
|
||||
CPPUNIT_ASSERT_EQUAL(expectedCreateTimestamp,
|
||||
metaData.GetCreateTimestamp());
|
||||
CPPUNIT_ASSERT_EQUAL(expectedDataModifiedTimestamp,
|
||||
metaData.GetDataModifiedTimestamp());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StandardMetaDataJsonEventListenerTest::TestTopLevel()
|
||||
{
|
||||
TestGeneric(INPUT_TOP_LEVEL, "$", B_OK, 123456L, 789012L);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StandardMetaDataJsonEventListenerTest::TestIcon()
|
||||
{
|
||||
TestGeneric(INPUT_ICON, "$", B_OK, 1501456139480ULL, 1500536391000ULL);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StandardMetaDataJsonEventListenerTest::TestThirdLevel()
|
||||
{
|
||||
TestGeneric(INPUT_THIRD_LEVEL, "$.rink.tonk", B_OK, 665544L, 554433L);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StandardMetaDataJsonEventListenerTest::TestBroken()
|
||||
{
|
||||
TestGeneric(INPUT_BROKEN, "$", B_BAD_DATA, 0L, 0L);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ void
|
||||
StandardMetaDataJsonEventListenerTest::AddTests(BTestSuite& parent)
|
||||
{
|
||||
CppUnit::TestSuite& suite = *new CppUnit::TestSuite(
|
||||
"StandardMetaDataJsonEventListenerTest");
|
||||
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<StandardMetaDataJsonEventListenerTest>(
|
||||
"StandardMetaDataJsonEventListenerTest::TestTopLevel",
|
||||
&StandardMetaDataJsonEventListenerTest::TestTopLevel));
|
||||
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<StandardMetaDataJsonEventListenerTest>(
|
||||
"StandardMetaDataJsonEventListenerTest::TestIcon",
|
||||
&StandardMetaDataJsonEventListenerTest::TestIcon));
|
||||
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<StandardMetaDataJsonEventListenerTest>(
|
||||
"StandardMetaDataJsonEventListenerTest::TestThirdLevel",
|
||||
&StandardMetaDataJsonEventListenerTest::TestThirdLevel));
|
||||
|
||||
suite.addTest(
|
||||
new CppUnit::TestCaller<StandardMetaDataJsonEventListenerTest>(
|
||||
"StandardMetaDataJsonEventListenerTest::TestBroken",
|
||||
&StandardMetaDataJsonEventListenerTest::TestBroken));
|
||||
|
||||
parent.addTest("StandardMetaDataJsonEventListenerTest", &suite);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef STANDARD_META_DATA_JSON_EVENT_LISTENER_TEST_H
|
||||
#define STANDARD_META_DATA_JSON_EVENT_LISTENER_TEST_H
|
||||
|
||||
|
||||
#include <TestCase.h>
|
||||
#include <TestSuite.h>
|
||||
|
||||
|
||||
class StandardMetaDataJsonEventListenerTest : public CppUnit::TestCase {
|
||||
public:
|
||||
StandardMetaDataJsonEventListenerTest();
|
||||
virtual ~StandardMetaDataJsonEventListenerTest();
|
||||
|
||||
void TestTopLevel();
|
||||
void TestIcon();
|
||||
void TestThirdLevel();
|
||||
void TestBroken();
|
||||
|
||||
static void AddTests(BTestSuite& suite);
|
||||
|
||||
private:
|
||||
void TestGeneric(const char* input,
|
||||
const char* jsonPath,
|
||||
status_t expectedStatus,
|
||||
uint64_t expectedCreateTimestamp,
|
||||
uint64_t expectedDataModifiedTimestamp);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // STANDARD_META_DATA_JSON_EVENT_LISTENER_TEST_H
|
Loading…
Reference in New Issue
Block a user