Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion Common/columnencoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ private: ColumnEncoder() { invalidateAll(); }
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 +88,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
1 change: 1 addition & 0 deletions CommonData/databridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "timers.h"

DataBridge::DataBridge(unsigned long sessionID, bool useMemory)
: _extraEncodings(new ColumnEncoder(ExtraOptionsPrefix))
{
JASPTIMER_START(TempFiles Attach);
TempFiles::attach(sessionID);
Expand Down
10 changes: 10 additions & 0 deletions CommonData/databridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@

#include "dataset.h"

#include <memory>

class ColumnEncoder;

class DataBridge
{
public:
Expand All @@ -44,6 +48,8 @@ class DataBridge
void provideSpecificFileName( const std::string & specificName, std::string & root, std::string & relativePath);
int dataSetRowCount() { return static_cast<int>(provideAndUpdateDataSet()->rowCount()); }
void updateOptionsAccordingToMeta(Json::Value & options);
ColumnEncoder * extraEncodings() { return _extraEncodings.get(); }
const ColumnEncoder * extraEncodings() const { return _extraEncodings.get(); }

protected:
bool isColumnNameOk(const std::string & columnName);
Expand All @@ -54,6 +60,10 @@ class DataBridge
DatabaseInterface * _db = nullptr;
int _analysisId = -1;
std::function<void()> _datasetProvidedCallback;

private:
static constexpr const char * ExtraOptionsPrefix = "JaspExtraOptions_";
std::unique_ptr<ColumnEncoder> _extraEncodings;
};

#endif // DATABRIDGE_H
14 changes: 6 additions & 8 deletions CommonData/rbridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ void rbridge_setDataBridge(DataBridge * dataBridge)
{
data_bridge = dataBridge;
rbridge_dataSet = nullptr;
extraEncodings = dataBridge ? dataBridge->extraEncodings() : nullptr;
}

void rbridge_clearDataBridge()
{
data_bridge = nullptr;
rbridge_dataSet = nullptr;
rbridge_setDataBridge(nullptr);
}

const std::string jaspBaseDistributionSamplersR =
Expand Down Expand Up @@ -102,14 +102,12 @@ const std::string jaspBaseTransformPowerR =
#include "jaspBase_transformPower.h"
;

void rbridge_init(DataBridge * dataBridge, sendFuncDef sendToDesktopFunction, pollMessagesFuncDef pollMessagesFunction, ColumnEncoder * extraEncoder, const char * resultFont, bool insideJasp)
void rbridge_init(DataBridge * dataBridge, sendFuncDef sendToDesktopFunction, pollMessagesFuncDef pollMessagesFunction, const char * resultFont, bool insideJasp)
{
JASPTIMER_SCOPE(rbridge_init);

Log::log() << "Setting DataBridge and extraEncodings." << std::endl;
rbridge_setDataBridge(dataBridge);

Log::log() << "Setting extraEncodings." << std::endl;
extraEncodings = extraEncoder;

Log::log() << "Collecting RBridgeCallBacks." << std::endl;
RBridgeCallBacks callbacks = {
Expand Down Expand Up @@ -211,12 +209,12 @@ extern "C" int STDCALL rbridge_decodeColumnType(const char * in)

extern "C" bool STDCALL rbridge_shouldEncodeColumnName(const char * in)
{
return ColumnEncoder::columnEncoder()->shouldEncode(in);
return (extraEncodings && extraEncodings->shouldEncode(in)) || ColumnEncoder::columnEncoder()->shouldEncode(in);
}

extern "C" bool STDCALL rbridge_shouldDecodeColumnName(const char * in)
{
return ColumnEncoder::columnEncoder()->shouldDecode(in);
return (extraEncodings && extraEncodings->shouldDecode(in)) || ColumnEncoder::columnEncoder()->shouldDecode(in);
}

extern "C" const char * STDCALL rbridge_encodeAllColumnNames(const char * in)
Expand Down
2 changes: 1 addition & 1 deletion CommonData/rbridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ extern "C" {

void rbridge_setDataBridge(DataBridge * dataBridge);
void rbridge_clearDataBridge();
void rbridge_init(DataBridge * dataBridge, sendFuncDef sendToDesktopFunction, pollMessagesFuncDef pollMessagesFunction, ColumnEncoder * encoder, const char * resultFont, bool insideJasp = true);
void rbridge_init(DataBridge * dataBridge, sendFuncDef sendToDesktopFunction, pollMessagesFuncDef pollMessagesFunction, const char * resultFont, bool insideJasp = true);

void rbridge_memoryCleaning();

Expand Down
6 changes: 2 additions & 4 deletions Engine/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ Engine::Engine(int slaveNo, unsigned long parentPID)
JASPTIMER_SCOPE(Engine Constructor);
assert(_EngineInstance == NULL);
_EngineInstance = this;

_extraEncodings = new ColumnEncoder("JaspExtraOptions_");
}

void Engine::initialize()
Expand All @@ -88,7 +86,7 @@ void Engine::initialize()
std::string memoryName = "JASP-IPC-" + std::to_string(_parentPID);
_channel = new IPCChannel(memoryName, _engineNum, true);

rbridge_init(this, SendFunctionForJaspresults, PollMessagesFunctionForJaspResults, _extraEncodings, _resultFont.c_str());
rbridge_init(this, SendFunctionForJaspresults, PollMessagesFunctionForJaspResults, _resultFont.c_str());

Log::log() << "rbridge_init completed" << std::endl;

Expand Down Expand Up @@ -679,7 +677,7 @@ void Engine::receiveAnalysisMessage(const Json::Value & jsonRequest)

Log::log(false) << _analysisTitle << " with ID " << _analysisId << std::endl;

_extraEncodings->setCurrentNamesFromOptionsMeta(optionsEnc);
extraEncodings()->setCurrentNamesFromOptionsMeta(optionsEnc);

_analysisOptions = optionsEnc; //store unencoded
}
Expand Down
1 change: 0 additions & 1 deletion Engine/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ class Engine : public DataBridge
const int _engineNum;
const unsigned long _parentPID;
IPCChannel * _channel = nullptr;
ColumnEncoder * _extraEncodings = nullptr;
engineState _engineState = engineState::initializing,
_lastRequest = engineState::initializing;
Status _analysisStatus = Status::empty;
Expand Down
4 changes: 4 additions & 0 deletions SyntaxInterface/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ target_compile_definitions(
JASP_R_INTERFACE_LIBRARY
)

if(MSVC)
target_compile_options(SyntaxInterface PRIVATE /EHsc)
endif()

if(USE_QT_STATIC_LIBS)
target_compile_definitions(
SyntaxInterface
Expand Down
Loading
Loading