Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Common/columnencoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,7 @@ void ColumnEncoder::_addTypeToColumnNamesInOptionsRecursively(Json::Value & opti

ColumnEncoder::colsPlusTypes ColumnEncoder::encodeColumnNamesinOptions(Json::Value & options, bool preloadingData)
{
columnEncoder();
colsPlusTypes getTheseCols;

_addTypeToColumnNamesInOptionsRecursively(options, preloadingData, getTheseCols);
Expand Down
4 changes: 3 additions & 1 deletion Common/columnencoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,12 @@ private: ColumnEncoder() { invalidateAll(); }

static colVec columnNames();
static colVec columnNamesEncoded();
static const char* extraOptionsPrefix() { return "JaspExtraOptions_"; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now a double definition. Very ugly. Should be moved to DataBridge instead. That way it covers both engine and syntaxbridge

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 3c0575c. I moved this out of ColumnEncoder and made DataBridge the single owner of the extra-options encoder.

Concretely:

  • removed ColumnEncoder::extraOptionsPrefix()
  • added a bridge-owned extra encoder in DataBridge
  • removed Engine::_extraEncodings
  • removed the separate SyntaxInterface gl_extraEncodings
  • changed rbridge_init() to derive the extra encoder from the active DataBridge, and rbridge_setDataBridge() now refreshes that borrowed pointer whenever SyntaxInterface recreates its DataBridge

So Engine and SyntaxInterface now share the same ownership model through DataBridge, and SyntaxBridge only borrows the encoder for context capture/decode.


bool shouldEncode(const std::string & in);
bool shouldDecode(const std::string & in);
void setCurrentNames(const colTypeMap & names);
const colTypeMap& currentNames() const { return _dataSetTypes; }
void updateColumnTypesOnly(const colTypeMap & names);
void setCurrentNames(const std::vector<std::string> & names, bool generateTypesEncoding=true); ///< Do not use! Deprecated
void setCurrentColumnTypePerName(const colTypeMap & theMap); ///< Do not use! Deprecated
Expand All @@ -87,7 +89,7 @@ private: ColumnEncoder() { invalidateAll(); }
static std::string encodeAll(const std::string & text) { return replaceAll(text, encodingMap(), originalNames()); }

///Replace all occurences of encoded columnNames in a string by their decoded versions, regardless of word boundaries or parentheses.
static std::string decodeAll(const std::string & text) { return replaceAll(text, decodingMap(), encodedNames()); }
static std::string decodeAll(const std::string & text) { columnEncoder(); return replaceAll(text, decodingMap(), encodedNames()); }

///Replace all occurences of columnNames in a string by their encoded versions in all json-names and string-values, regardless of word boundaries or parentheses.
static void encodeJson(Json::Value & json, bool replaceNames = false, bool replaceStrict = false);
Expand Down
144 changes: 144 additions & 0 deletions Common/columnencodercontext.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//
// Copyright (C) 2013-2025 University of Amsterdam
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//

#include "columnencodercontext.h"

#include <stdexcept>

static Json::Value columnTypesToJson(const ColumnEncoder::colTypeMap & columnTypes)
{
Json::Value columns(Json::arrayValue);
for(const auto & nameType : columnTypes)
{
Json::Value column(Json::objectValue);
column["name"] = nameType.first;
column["type"] = columnTypeToString(nameType.second);
columns.append(column);
}

return columns;
}

static ColumnEncoder::colTypeMap columnTypesFromJson(const Json::Value & columns, const char * fieldName)
{
ColumnEncoder::colTypeMap columnTypes;

if(columns.isNull())
return columnTypes;
if(!columns.isArray())
throw std::runtime_error(std::string("Column encoder context field '") + fieldName + "' must be an array.");

for(const Json::Value & column : columns)
{
if(!column.isObject() || !column["name"].isString() || !column["type"].isString())
throw std::runtime_error(std::string("Column encoder context field '") + fieldName + "' must contain objects with string 'name' and 'type' fields.");

columnTypes[column["name"].asString()] = columnTypeFromString(column["type"].asString());
}

return columnTypes;
}

static Json::Value parsePayloadJson(const char * payloadJson)
{
if(!payloadJson)
throw std::runtime_error("Cannot decode column text from a null JSON payload.");

Json::Value payload;
Json::Reader reader;
if(!reader.parse(payloadJson, payload))
throw std::runtime_error("Could not parse column text JSON payload.");

return payload;
}

ColumnEncoderContext::ColumnEncoderContext(const ColumnEncoder::colTypeMap & columns, const ColumnEncoder::colTypeMap & extra)
: _columns(columns), _extra(extra), _supplied(true)
{
}

ColumnEncoderContext ColumnEncoderContext::fromJson(const Json::Value & context)
{
if(!context.isObject())
throw std::runtime_error("Column encoder context must be a JSON object.");

if(!context.isMember("version") || !context["version"].isInt())
throw std::runtime_error("Column encoder context must contain integer version 1.");
if(context["version"].asInt() != Version)
throw std::runtime_error("Unsupported column encoder context version.");

return ColumnEncoderContext(
columnTypesFromJson(context["columns"], "columns"),
columnTypesFromJson(context["extra"], "extra")
);
}

ColumnEncoderContext ColumnEncoderContext::fromJsonString(const char * contextJson)
{
if(!contextJson || std::string(contextJson).empty())
return ColumnEncoderContext();

Json::Value context;
Json::Reader reader;
if(!reader.parse(contextJson, context))
throw std::runtime_error("Could not parse column encoder context JSON.");

return fromJson(context);
}

Json::Value ColumnEncoderContext::toJson() const
{
Json::Value context(Json::objectValue);
context["version"] = Version;
context["columns"] = columnTypesToJson(_columns);
context["extra"] = columnTypesToJson(_extra);

return context;
}

ScopedColumnEncoderContext::ScopedColumnEncoderContext(const ColumnEncoderContext & context, ColumnEncoder & extraEncoder)
: _supplied(context.supplied()), _extraEncoder(extraEncoder)
{
if(!_supplied)
return;

_previousColumns = ColumnEncoder::columnEncoder()->currentNames();
_previousExtra = _extraEncoder.currentNames();

ColumnEncoder::columnEncoder()->setCurrentNames(context.columns());
_extraEncoder.setCurrentNames(context.extra());
}

ScopedColumnEncoderContext::~ScopedColumnEncoderContext()
{
if(!_supplied)
return;

ColumnEncoder::columnEncoder()->setCurrentNames(_previousColumns);
_extraEncoder.setCurrentNames(_previousExtra);
}

Json::Value decodeColumnJson(const char * payloadJson, const char * encoderContextJson, ColumnEncoder & extraEncoder, bool replaceNames)
{
Json::Value payload = parsePayloadJson(payloadJson);
ColumnEncoderContext context = ColumnEncoderContext::fromJsonString(encoderContextJson);
ScopedColumnEncoderContext scopedContext(context, extraEncoder);

ColumnEncoder::decodeJson(payload, replaceNames);

return payload;
}
61 changes: 61 additions & 0 deletions Common/columnencodercontext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// Copyright (C) 2013-2025 University of Amsterdam
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//

#ifndef COLUMNENCODERCONTEXT_H
#define COLUMNENCODERCONTEXT_H

#include "columnencoder.h"

class ColumnEncoderContext
{
public:
static constexpr int Version = 1;

ColumnEncoderContext() = default;
ColumnEncoderContext(const ColumnEncoder::colTypeMap & columns, const ColumnEncoder::colTypeMap & extra);

static ColumnEncoderContext fromJson(const Json::Value & context);
static ColumnEncoderContext fromJsonString(const char * contextJson);

Json::Value toJson() const;

const ColumnEncoder::colTypeMap& columns() const { return _columns; }
const ColumnEncoder::colTypeMap& extra() const { return _extra; }
bool supplied() const { return _supplied; }

private:
ColumnEncoder::colTypeMap _columns;
ColumnEncoder::colTypeMap _extra;
bool _supplied = false;
};

class ScopedColumnEncoderContext
{
public:
ScopedColumnEncoderContext(const ColumnEncoderContext & context, ColumnEncoder & extraEncoder);
~ScopedColumnEncoderContext();

private:
bool _supplied = false;
ColumnEncoder & _extraEncoder;
ColumnEncoder::colTypeMap _previousColumns;
ColumnEncoder::colTypeMap _previousExtra;
};

Json::Value decodeColumnJson(const char * payloadJson, const char * encoderContextJson, ColumnEncoder & extraEncoder, bool replaceNames = true);

#endif // COLUMNENCODERCONTEXT_H
2 changes: 1 addition & 1 deletion Engine/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Engine::Engine(int slaveNo, unsigned long parentPID)
assert(_EngineInstance == NULL);
_EngineInstance = this;

_extraEncodings = new ColumnEncoder("JaspExtraOptions_");
_extraEncodings = new ColumnEncoder(ColumnEncoder::extraOptionsPrefix());
}

void Engine::initialize()
Expand Down
Loading
Loading