mirror of
https://github.com/vczh-libraries/Release.git
synced 2026-08-17 17:31:44 +08:00
5906 lines
196 KiB
C++
5906 lines
196 KiB
C++
/***********************************************************************
|
|
THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY
|
|
DEVELOPER: Zihan Chen(vczh)
|
|
***********************************************************************/
|
|
#include "VlppWorkflowLibrary.h"
|
|
|
|
/***********************************************************************
|
|
.\WFLIBRARYCPPHELPER.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace __vwsn
|
|
{
|
|
|
|
/***********************************************************************
|
|
CreateArray
|
|
***********************************************************************/
|
|
|
|
CreateArray::CreateArray()
|
|
:list(IValueArray::Create())
|
|
{
|
|
}
|
|
|
|
CreateArray::CreateArray(Ptr<IValueArray> _list)
|
|
:list(_list)
|
|
{
|
|
}
|
|
|
|
/***********************************************************************
|
|
CreateList
|
|
***********************************************************************/
|
|
|
|
CreateList::CreateList()
|
|
:list(IValueList::Create())
|
|
{
|
|
}
|
|
|
|
CreateList::CreateList(Ptr<IValueList> _list)
|
|
:list(_list)
|
|
{
|
|
}
|
|
|
|
/***********************************************************************
|
|
CreateObservableList
|
|
***********************************************************************/
|
|
|
|
CreateObservableList::CreateObservableList()
|
|
:list(IValueObservableList::Create())
|
|
{
|
|
}
|
|
|
|
CreateObservableList::CreateObservableList(Ptr<IValueObservableList> _list)
|
|
:list(_list)
|
|
{
|
|
}
|
|
|
|
/***********************************************************************
|
|
CreateDictionary
|
|
***********************************************************************/
|
|
|
|
CreateDictionary::CreateDictionary()
|
|
:dictionary(IValueDictionary::Create())
|
|
{
|
|
}
|
|
|
|
CreateDictionary::CreateDictionary(Ptr<IValueDictionary> _dictionary)
|
|
:dictionary(_dictionary)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
.\WFLIBRARYPREDEFINED.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
using namespace collections;
|
|
|
|
namespace reflection
|
|
{
|
|
namespace description
|
|
{
|
|
|
|
/***********************************************************************
|
|
CoroutineResult
|
|
***********************************************************************/
|
|
|
|
Value CoroutineResult::GetResult()
|
|
{
|
|
return result;
|
|
}
|
|
|
|
void CoroutineResult::SetResult(const Value& value)
|
|
{
|
|
result = value;
|
|
}
|
|
|
|
Ptr<IValueException> CoroutineResult::GetFailure()
|
|
{
|
|
return failure;
|
|
}
|
|
|
|
void CoroutineResult::SetFailure(Ptr<IValueException> value)
|
|
{
|
|
failure = value;
|
|
}
|
|
|
|
/***********************************************************************
|
|
EnumerableCoroutine
|
|
***********************************************************************/
|
|
|
|
class CoroutineEnumerator : public Object, public virtual EnumerableCoroutine::IImpl, public Description<CoroutineEnumerator>
|
|
{
|
|
protected:
|
|
EnumerableCoroutine::Creator creator;
|
|
Ptr<ICoroutine> coroutine;
|
|
Value current;
|
|
vint index = -1;
|
|
Ptr<IValueEnumerator> joining;
|
|
|
|
public:
|
|
CoroutineEnumerator(const EnumerableCoroutine::Creator& _creator)
|
|
:creator(_creator)
|
|
{
|
|
}
|
|
|
|
Value GetCurrent()override
|
|
{
|
|
return current;
|
|
}
|
|
|
|
vint GetIndex()override
|
|
{
|
|
return index;
|
|
}
|
|
|
|
bool Next()override
|
|
{
|
|
if (!coroutine)
|
|
{
|
|
coroutine = creator(this);
|
|
}
|
|
|
|
while (coroutine->GetStatus() == CoroutineStatus::Waiting)
|
|
{
|
|
if (joining)
|
|
{
|
|
if (joining->Next())
|
|
{
|
|
current = joining->GetCurrent();
|
|
index++;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
joining = nullptr;
|
|
}
|
|
}
|
|
|
|
coroutine->Resume(true, nullptr);
|
|
if (coroutine->GetStatus() != CoroutineStatus::Waiting)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!joining)
|
|
{
|
|
index++;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void OnYield(const Value& value)override
|
|
{
|
|
current = value;
|
|
joining = nullptr;
|
|
}
|
|
|
|
void OnJoin(Ptr<IValueEnumerable> value)override
|
|
{
|
|
if (!value)
|
|
{
|
|
throw Exception(L"Cannot join a null collection.");
|
|
}
|
|
current = Value();
|
|
joining = value->CreateEnumerator();
|
|
}
|
|
};
|
|
|
|
class CoroutineEnumerable : public Object, public virtual IValueEnumerable, public Description<CoroutineEnumerable>
|
|
{
|
|
protected:
|
|
EnumerableCoroutine::Creator creator;
|
|
|
|
public:
|
|
CoroutineEnumerable(const EnumerableCoroutine::Creator& _creator)
|
|
:creator(_creator)
|
|
{
|
|
}
|
|
|
|
Ptr<IValueEnumerator> CreateEnumerator()override
|
|
{
|
|
return Ptr(new CoroutineEnumerator(creator));
|
|
}
|
|
};
|
|
|
|
void EnumerableCoroutine::YieldAndPause(IImpl* impl, const Value& value)
|
|
{
|
|
impl->OnYield(value);
|
|
}
|
|
|
|
void EnumerableCoroutine::JoinAndPause(IImpl* impl, Ptr<IValueEnumerable> value)
|
|
{
|
|
impl->OnJoin(value);
|
|
}
|
|
|
|
void EnumerableCoroutine::ReturnAndExit(IImpl* impl)
|
|
{
|
|
}
|
|
|
|
Ptr<IValueEnumerable> EnumerableCoroutine::Create(const Creator& creator)
|
|
{
|
|
return Ptr(new CoroutineEnumerable(creator));
|
|
}
|
|
|
|
/***********************************************************************
|
|
AsyncContext
|
|
***********************************************************************/
|
|
|
|
AsyncContext::AsyncContext(const Value& _context)
|
|
:context(_context)
|
|
{
|
|
}
|
|
|
|
AsyncContext::~AsyncContext()
|
|
{
|
|
}
|
|
|
|
bool AsyncContext::IsCancelled()
|
|
{
|
|
SPIN_LOCK(lock)
|
|
{
|
|
return cancelled;
|
|
}
|
|
CHECK_FAIL(L"Not reachable");
|
|
}
|
|
|
|
bool AsyncContext::Cancel()
|
|
{
|
|
SPIN_LOCK(lock)
|
|
{
|
|
if (cancelled) return false;
|
|
cancelled = true;
|
|
return true;
|
|
}
|
|
CHECK_FAIL(L"Not reachable");
|
|
}
|
|
|
|
const description::Value& AsyncContext::GetContext()
|
|
{
|
|
SPIN_LOCK(lock)
|
|
{
|
|
return context;
|
|
}
|
|
CHECK_FAIL(L"Not reachable");
|
|
}
|
|
|
|
void AsyncContext::SetContext(const description::Value& value)
|
|
{
|
|
SPIN_LOCK(lock)
|
|
{
|
|
context = value;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
DelayAsync
|
|
***********************************************************************/
|
|
|
|
class DelayAsync : public Object, public virtual IAsync, public Description<DelayAsync>
|
|
{
|
|
protected:
|
|
SpinLock lock;
|
|
vint milliseconds;
|
|
AsyncStatus status = AsyncStatus::Ready;
|
|
|
|
public:
|
|
DelayAsync(vint _milliseconds)
|
|
:milliseconds(_milliseconds)
|
|
{
|
|
}
|
|
|
|
AsyncStatus GetStatus()override
|
|
{
|
|
return status;
|
|
}
|
|
|
|
bool Execute(const Func<void(Ptr<CoroutineResult>)>& _callback, Ptr<AsyncContext> context)override
|
|
{
|
|
SPIN_LOCK(lock)
|
|
{
|
|
if (status != AsyncStatus::Ready) return false;
|
|
status = AsyncStatus::Executing;
|
|
IAsyncScheduler::GetSchedulerForCurrentThread()->DelayExecute([async = Ptr<DelayAsync>(this), callback = _callback]()
|
|
{
|
|
if (callback)
|
|
{
|
|
callback(nullptr);
|
|
}
|
|
}, milliseconds);
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
Ptr<IAsync> IAsync::Delay(vint milliseconds)
|
|
{
|
|
return Ptr(new DelayAsync(milliseconds));
|
|
}
|
|
|
|
/***********************************************************************
|
|
FutureAndPromiseAsync
|
|
***********************************************************************/
|
|
|
|
class FutureAndPromiseAsync : public virtual IFuture, public virtual IPromise, public Description<FutureAndPromiseAsync>
|
|
{
|
|
public:
|
|
SpinLock lock;
|
|
AsyncStatus status = AsyncStatus::Ready;
|
|
Ptr<CoroutineResult> cr;
|
|
Func<void(Ptr<CoroutineResult>)> callback;
|
|
|
|
void ExecuteCallbackAndClear()
|
|
{
|
|
status = AsyncStatus::Stopped;
|
|
if (callback)
|
|
{
|
|
callback(cr);
|
|
}
|
|
cr = nullptr;
|
|
callback = {};
|
|
}
|
|
|
|
template<typename F>
|
|
bool Send(F f)
|
|
{
|
|
SPIN_LOCK(lock)
|
|
{
|
|
if (status == AsyncStatus::Stopped || cr) return false;
|
|
cr = Ptr(new CoroutineResult);
|
|
f();
|
|
if (status == AsyncStatus::Executing)
|
|
{
|
|
ExecuteCallbackAndClear();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
AsyncStatus GetStatus()override
|
|
{
|
|
return status;
|
|
}
|
|
|
|
bool Execute(const Func<void(Ptr<CoroutineResult>)>& _callback, Ptr<AsyncContext> context)override
|
|
{
|
|
SPIN_LOCK(lock)
|
|
{
|
|
if (status != AsyncStatus::Ready) return false;
|
|
callback = _callback;
|
|
if (cr)
|
|
{
|
|
ExecuteCallbackAndClear();
|
|
}
|
|
else
|
|
{
|
|
status = AsyncStatus::Executing;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Ptr<IPromise> GetPromise()override
|
|
{
|
|
return Ptr(this);
|
|
}
|
|
|
|
bool SendResult(const Value& result)override
|
|
{
|
|
return Send([=, this]()
|
|
{
|
|
cr->SetResult(result);
|
|
});
|
|
}
|
|
|
|
bool SendFailure(Ptr<IValueException> failure)override
|
|
{
|
|
return Send([=, this]()
|
|
{
|
|
cr->SetFailure(failure);
|
|
});
|
|
}
|
|
};
|
|
|
|
Ptr<IFuture> IFuture::Create()
|
|
{
|
|
return Ptr(new FutureAndPromiseAsync);
|
|
}
|
|
|
|
/***********************************************************************
|
|
IAsyncScheduler
|
|
***********************************************************************/
|
|
|
|
class AsyncSchedulerMap
|
|
{
|
|
public:
|
|
Dictionary<vint, Ptr<IAsyncScheduler>> schedulers;
|
|
Ptr<IAsyncScheduler> defaultScheduler;
|
|
};
|
|
|
|
AsyncSchedulerMap* asyncSchedulerMap = nullptr;
|
|
SpinLock asyncSchedulerLock;
|
|
|
|
#define ENSURE_ASYNC_SCHEDULER_MAP\
|
|
if (!asyncSchedulerMap) asyncSchedulerMap = new AsyncSchedulerMap;
|
|
|
|
#define DISPOSE_ASYNC_SCHEDULER_MAP_IF_NECESSARY\
|
|
if (asyncSchedulerMap->schedulers.Count() == 0 && !asyncSchedulerMap->defaultScheduler)\
|
|
{\
|
|
delete asyncSchedulerMap;\
|
|
asyncSchedulerMap = nullptr;\
|
|
}\
|
|
|
|
void IAsyncScheduler::RegisterDefaultScheduler(Ptr<IAsyncScheduler> scheduler)
|
|
{
|
|
SPIN_LOCK(asyncSchedulerLock)
|
|
{
|
|
ENSURE_ASYNC_SCHEDULER_MAP
|
|
CHECK_ERROR(!asyncSchedulerMap->defaultScheduler, L"IAsyncScheduler::RegisterDefaultScheduler()#A default scheduler has already been registered.");
|
|
asyncSchedulerMap->defaultScheduler = scheduler;
|
|
}
|
|
}
|
|
|
|
void IAsyncScheduler::RegisterSchedulerForCurrentThread(Ptr<IAsyncScheduler> scheduler)
|
|
{
|
|
SPIN_LOCK(asyncSchedulerLock)
|
|
{
|
|
ENSURE_ASYNC_SCHEDULER_MAP
|
|
CHECK_ERROR(!asyncSchedulerMap->schedulers.Keys().Contains(Thread::GetCurrentThreadId()), L"IAsyncScheduler::RegisterDefaultScheduler()#A scheduler for this thread has already been registered.");
|
|
asyncSchedulerMap->schedulers.Add(Thread::GetCurrentThreadId(), scheduler);
|
|
}
|
|
}
|
|
|
|
Ptr<IAsyncScheduler> IAsyncScheduler::UnregisterDefaultScheduler()
|
|
{
|
|
Ptr<IAsyncScheduler> scheduler;
|
|
SPIN_LOCK(asyncSchedulerLock)
|
|
{
|
|
if (asyncSchedulerMap)
|
|
{
|
|
scheduler = asyncSchedulerMap->defaultScheduler;
|
|
asyncSchedulerMap->defaultScheduler = nullptr;
|
|
DISPOSE_ASYNC_SCHEDULER_MAP_IF_NECESSARY
|
|
}
|
|
}
|
|
return scheduler;
|
|
}
|
|
|
|
Ptr<IAsyncScheduler> IAsyncScheduler::UnregisterSchedulerForCurrentThread()
|
|
{
|
|
Ptr<IAsyncScheduler> scheduler;
|
|
SPIN_LOCK(asyncSchedulerLock)
|
|
{
|
|
if (asyncSchedulerMap)
|
|
{
|
|
vint index = asyncSchedulerMap->schedulers.Keys().IndexOf(Thread::GetCurrentThreadId());
|
|
if (index != -1)
|
|
{
|
|
scheduler = asyncSchedulerMap->schedulers.Values()[index];
|
|
asyncSchedulerMap->schedulers.Remove(Thread::GetCurrentThreadId());
|
|
}
|
|
DISPOSE_ASYNC_SCHEDULER_MAP_IF_NECESSARY
|
|
}
|
|
}
|
|
return scheduler;
|
|
}
|
|
|
|
#undef ENSURE_ASYNC_SCHEDULER_MAP
|
|
#undef DISPOSE_ASYNC_SCHEDULER_MAP_IF_NECESSARY
|
|
|
|
Ptr<IAsyncScheduler> IAsyncScheduler::GetSchedulerForCurrentThread()
|
|
{
|
|
Ptr<IAsyncScheduler> scheduler;
|
|
SPIN_LOCK(asyncSchedulerLock)
|
|
{
|
|
CHECK_ERROR(asyncSchedulerMap != nullptr, L"IAsyncScheduler::GetSchedulerForCurrentThread()#There is no scheduler registered for the current thread.");
|
|
vint index = asyncSchedulerMap->schedulers.Keys().IndexOf(Thread::GetCurrentThreadId());
|
|
if (index != -1)
|
|
{
|
|
scheduler = asyncSchedulerMap->schedulers.Values()[index];
|
|
}
|
|
else if (asyncSchedulerMap->defaultScheduler)
|
|
{
|
|
scheduler = asyncSchedulerMap->defaultScheduler;
|
|
}
|
|
else
|
|
{
|
|
CHECK_FAIL(L"IAsyncScheduler::GetSchedulerForCurrentThread()#There is no scheduler registered for the current thread.");
|
|
}
|
|
}
|
|
return scheduler;
|
|
}
|
|
|
|
/***********************************************************************
|
|
AsyncCoroutine
|
|
***********************************************************************/
|
|
|
|
class CoroutineAsync : public Object, public virtual AsyncCoroutine::IImpl, public Description<CoroutineAsync>
|
|
{
|
|
protected:
|
|
Ptr<ICoroutine> coroutine;
|
|
AsyncCoroutine::Creator creator;
|
|
Ptr<IAsyncScheduler> scheduler;
|
|
Func<void(Ptr<CoroutineResult>)> callback;
|
|
Ptr<AsyncContext> context;
|
|
Value result;
|
|
|
|
public:
|
|
CoroutineAsync(AsyncCoroutine::Creator _creator)
|
|
:creator(_creator)
|
|
{
|
|
}
|
|
|
|
AsyncStatus GetStatus()override
|
|
{
|
|
if (!coroutine)
|
|
{
|
|
return AsyncStatus::Ready;
|
|
}
|
|
else if (coroutine->GetStatus() != CoroutineStatus::Stopped)
|
|
{
|
|
return AsyncStatus::Executing;
|
|
}
|
|
else
|
|
{
|
|
return AsyncStatus::Stopped;
|
|
}
|
|
}
|
|
|
|
bool Execute(const Func<void(Ptr<CoroutineResult>)>& _callback, Ptr<AsyncContext> _context)override
|
|
{
|
|
if (coroutine) return false;
|
|
scheduler = IAsyncScheduler::GetSchedulerForCurrentThread();
|
|
callback = _callback;
|
|
context = _context;
|
|
coroutine = creator(this);
|
|
OnContinue(nullptr);
|
|
return true;
|
|
}
|
|
|
|
Ptr<IAsyncScheduler> GetScheduler()override
|
|
{
|
|
return scheduler;
|
|
}
|
|
|
|
Ptr<AsyncContext> GetContext()override
|
|
{
|
|
if (!context)
|
|
{
|
|
context = Ptr(new AsyncContext);
|
|
}
|
|
return context;
|
|
}
|
|
|
|
void OnContinue(Ptr<CoroutineResult> output)override
|
|
{
|
|
scheduler->Execute([async = Ptr<CoroutineAsync>(this), output]()
|
|
{
|
|
async->coroutine->Resume(false, output);
|
|
if (async->coroutine->GetStatus() == CoroutineStatus::Stopped && async->callback)
|
|
{
|
|
auto result = Ptr(new CoroutineResult);
|
|
if (async->coroutine->GetFailure())
|
|
{
|
|
result->SetFailure(async->coroutine->GetFailure());
|
|
}
|
|
else
|
|
{
|
|
result->SetResult(async->result);
|
|
}
|
|
async->callback(result);
|
|
}
|
|
});
|
|
}
|
|
|
|
void OnReturn(const Value& value)override
|
|
{
|
|
result = value;
|
|
}
|
|
};
|
|
|
|
void AsyncCoroutine::AwaitAndRead(IImpl* impl, Ptr<IAsync> value)
|
|
{
|
|
value->Execute([async = Ptr<IImpl>(impl)](auto output)
|
|
{
|
|
async->OnContinue(output);
|
|
}, impl->GetContext());
|
|
}
|
|
|
|
void AsyncCoroutine::ReturnAndExit(IImpl* impl, const Value& value)
|
|
{
|
|
impl->OnReturn(value);
|
|
}
|
|
|
|
Ptr<AsyncContext> AsyncCoroutine::QueryContext(IImpl* impl)
|
|
{
|
|
return impl->GetContext();
|
|
}
|
|
|
|
Ptr<IAsync> AsyncCoroutine::Create(const Creator& creator)
|
|
{
|
|
return Ptr(new CoroutineAsync(creator));
|
|
}
|
|
void AsyncCoroutine::CreateAndRun(const Creator& creator)
|
|
{
|
|
Ptr(new CoroutineAsync(creator))->Execute(
|
|
[](Ptr<CoroutineResult> cr)
|
|
{
|
|
if (cr->GetFailure())
|
|
{
|
|
#pragma push_macro("GetMessage")
|
|
#if defined GetMessage
|
|
#undef GetMessage
|
|
#endif
|
|
throw Exception(cr->GetFailure()->GetMessage());
|
|
#pragma pop_macro("GetMessage")
|
|
}
|
|
}, nullptr);
|
|
}
|
|
|
|
/***********************************************************************
|
|
StateMachine
|
|
***********************************************************************/
|
|
|
|
void StateMachine::ResumeStateMachine()
|
|
{
|
|
Ptr<CoroutineResult> previousResult;
|
|
while (true)
|
|
{
|
|
if (stateMachineCoroutine == nullptr)
|
|
{
|
|
if (!stateMachineInitialized)
|
|
{
|
|
throw Exception(L"The state machine has not been initialized.");
|
|
}
|
|
if (stateMachineStopped)
|
|
{
|
|
throw Exception(L"The state machine has been stopped.");
|
|
}
|
|
stateMachineStopped = true;
|
|
|
|
if (previousResult)
|
|
{
|
|
if (auto failure = previousResult->GetFailure())
|
|
{
|
|
throw Exception(failure->GetMessage());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else if (stateMachineCoroutine->GetStatus() != CoroutineStatus::Stopped)
|
|
{
|
|
auto currentCoroutine = stateMachineCoroutine;
|
|
currentCoroutine->Resume(false, previousResult);
|
|
if (stateMachineCoroutine == currentCoroutine)
|
|
{
|
|
// wait for input
|
|
break;
|
|
}
|
|
else if (currentCoroutine->GetStatus() == CoroutineStatus::Stopped)
|
|
{
|
|
// leave a state machine
|
|
previousResult = Ptr(new CoroutineResult);
|
|
if (auto failure = currentCoroutine->GetFailure())
|
|
{
|
|
previousResult->SetFailure(failure);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// enter a state machine
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
StateMachine::StateMachine()
|
|
{
|
|
}
|
|
|
|
StateMachine::~StateMachine()
|
|
{
|
|
FinalizeAggregation();
|
|
}
|
|
|
|
CoroutineStatus StateMachine::GetStateMachineStatus()
|
|
{
|
|
if (stateMachineStopped)
|
|
{
|
|
return CoroutineStatus::Stopped;
|
|
}
|
|
else if (stateMachineCoroutine)
|
|
{
|
|
if (stateMachineCoroutine->GetStatus() == CoroutineStatus::Waiting)
|
|
{
|
|
return CoroutineStatus::Waiting;
|
|
}
|
|
else
|
|
{
|
|
return CoroutineStatus::Executing;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return CoroutineStatus::Waiting;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
Sys
|
|
***********************************************************************/
|
|
|
|
namespace system_sys
|
|
{
|
|
class ReverseEnumerable : public Object, public IValueEnumerable
|
|
{
|
|
protected:
|
|
Ptr<IValueReadonlyList> list;
|
|
|
|
class Enumerator : public Object, public IValueEnumerator
|
|
{
|
|
protected:
|
|
Ptr<IValueReadonlyList> list;
|
|
vint index;
|
|
|
|
public:
|
|
Enumerator(Ptr<IValueReadonlyList> _list)
|
|
:list(_list), index(_list->GetCount())
|
|
{
|
|
}
|
|
|
|
Value GetCurrent()
|
|
{
|
|
return list->Get(index);
|
|
}
|
|
|
|
vint GetIndex()
|
|
{
|
|
return list->GetCount() - 1 - index;
|
|
}
|
|
|
|
bool Next()
|
|
{
|
|
if (index <= 0) return false;
|
|
index--;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
public:
|
|
ReverseEnumerable(Ptr<IValueReadonlyList> _list)
|
|
:list(_list)
|
|
{
|
|
}
|
|
|
|
Ptr<IValueEnumerator> CreateEnumerator()override
|
|
{
|
|
return Ptr(new Enumerator(list));
|
|
}
|
|
};
|
|
}
|
|
|
|
DateTime Sys::GetLocalTime()
|
|
{
|
|
return DateTime::LocalTime();
|
|
}
|
|
|
|
DateTime Sys::GetUtcTime()
|
|
{
|
|
return DateTime::UtcTime();
|
|
}
|
|
|
|
DateTime Sys::ToLocalTime(DateTime dt)
|
|
{
|
|
return dt.ToLocalTime();
|
|
}
|
|
|
|
DateTime Sys::ToUtcTime(DateTime dt)
|
|
{
|
|
return dt.ToUtcTime();
|
|
}
|
|
|
|
DateTime Sys::Forward(DateTime dt, vuint64_t milliseconds)
|
|
{
|
|
return dt.Forward(milliseconds);
|
|
}
|
|
|
|
DateTime Sys::Backward(DateTime dt, vuint64_t milliseconds)
|
|
{
|
|
return dt.Backward(milliseconds);
|
|
}
|
|
|
|
DateTime Sys::CreateDateTime(vint year, vint month, vint day)
|
|
{
|
|
return DateTime::FromDateTime(year, month, day);
|
|
}
|
|
|
|
DateTime Sys::CreateDateTime(vint year, vint month, vint day, vint hour, vint minute, vint second, vint milliseconds)
|
|
{
|
|
return DateTime::FromDateTime(year, month, day, hour, minute, second, milliseconds);
|
|
}
|
|
|
|
Ptr<IValueEnumerable> Sys::ReverseEnumerable(Ptr<IValueEnumerable> value)
|
|
{
|
|
auto list = value.Cast<IValueReadonlyList>();
|
|
if (!list)
|
|
{
|
|
list = IValueList::Create(GetLazyList<Value>(value));
|
|
}
|
|
return Ptr(new system_sys::ReverseEnumerable(list));
|
|
}
|
|
|
|
#define DEFINE_COMPARE(TYPE)\
|
|
vint Sys::Compare(TYPE a, TYPE b)\
|
|
{\
|
|
auto result = a <=> b;\
|
|
if (result < 0) return -1;\
|
|
if (result > 0) return 1;\
|
|
return 0;\
|
|
}\
|
|
|
|
REFLECTION_PREDEFINED_PRIMITIVE_TYPES(DEFINE_COMPARE)
|
|
DEFINE_COMPARE(DateTime)
|
|
#undef DEFINE_COMPARE
|
|
|
|
/***********************************************************************
|
|
Math
|
|
***********************************************************************/
|
|
|
|
#define DEFINE_MINMAX(TYPE)\
|
|
TYPE Math::Min(TYPE a, TYPE b)\
|
|
{\
|
|
return Sys::Compare(a, b) < 0 ? a : b;\
|
|
}\
|
|
TYPE Math::Max(TYPE a, TYPE b)\
|
|
{\
|
|
return Sys::Compare(a, b) > 0 ? a : b;\
|
|
}\
|
|
|
|
REFLECTION_PREDEFINED_PRIMITIVE_TYPES(DEFINE_MINMAX)
|
|
DEFINE_MINMAX(DateTime)
|
|
#undef DEFINE_MINMAX
|
|
|
|
/***********************************************************************
|
|
Localization
|
|
***********************************************************************/
|
|
|
|
Locale Localization::Invariant()
|
|
{
|
|
return Locale::Invariant();
|
|
}
|
|
|
|
Locale Localization::System()
|
|
{
|
|
return Locale::SystemDefault();
|
|
}
|
|
|
|
Locale Localization::User()
|
|
{
|
|
return Locale::UserDefault();
|
|
}
|
|
|
|
collections::LazyList<Locale> Localization::Locales()
|
|
{
|
|
auto result = Ptr(new List<Locale>);
|
|
Locale::Enumerate(*result.Obj());
|
|
return result;
|
|
}
|
|
|
|
collections::LazyList<WString> Localization::GetShortDateFormats(Locale locale)
|
|
{
|
|
auto result = Ptr(new List<WString>);
|
|
locale.GetShortDateFormats(*result.Obj());
|
|
return result;
|
|
}
|
|
|
|
collections::LazyList<WString> Localization::GetLongDateFormats(Locale locale)
|
|
{
|
|
auto result = Ptr(new List<WString>);
|
|
locale.GetLongDateFormats(*result.Obj());
|
|
return result;
|
|
}
|
|
|
|
collections::LazyList<WString> Localization::GetYearMonthDateFormats(Locale locale)
|
|
{
|
|
auto result = Ptr(new List<WString>);
|
|
locale.GetYearMonthDateFormats(*result.Obj());
|
|
return result;
|
|
}
|
|
|
|
collections::LazyList<WString> Localization::GetLongTimeFormats(Locale locale)
|
|
{
|
|
auto result = Ptr(new List<WString>);
|
|
locale.GetLongTimeFormats(*result.Obj());
|
|
return result;
|
|
}
|
|
|
|
collections::LazyList<WString> Localization::GetShortTimeFormats(Locale locale)
|
|
{
|
|
auto result = Ptr(new List<WString>);
|
|
locale.GetShortTimeFormats(*result.Obj());
|
|
return result;
|
|
}
|
|
|
|
WString Localization::GetShortDayOfWeekName(Locale locale, vint dayOfWeek)
|
|
{
|
|
return locale.GetShortDayOfWeekName(dayOfWeek);
|
|
}
|
|
|
|
WString Localization::GetLongDayOfWeekName(Locale locale, vint dayOfWeek)
|
|
{
|
|
return locale.GetLongDayOfWeekName(dayOfWeek);
|
|
}
|
|
|
|
WString Localization::GetShortMonthName(Locale locale, vint month)
|
|
{
|
|
return locale.GetShortMonthName(month);
|
|
}
|
|
|
|
WString Localization::GetLongMonthName(Locale locale, vint month)
|
|
{
|
|
return locale.GetLongMonthName(month);
|
|
}
|
|
|
|
WString Localization::FormatDate(Locale locale, const WString& format, DateTime date)
|
|
{
|
|
return locale.FormatDate(format, date);
|
|
}
|
|
|
|
WString Localization::FormatTime(Locale locale, const WString& format, DateTime date)
|
|
{
|
|
return locale.FormatTime(format, date);
|
|
}
|
|
|
|
WString Localization::FormatNumber(Locale locale, const WString& number)
|
|
{
|
|
return locale.FormatNumber(number);
|
|
}
|
|
|
|
WString Localization::FormatCurrency(Locale locale, const WString& number)
|
|
{
|
|
return locale.FormatCurrency(number);
|
|
}
|
|
|
|
/***********************************************************************
|
|
Versioning
|
|
***********************************************************************/
|
|
|
|
Versioning::Versioning()
|
|
{
|
|
}
|
|
|
|
Versioning::~Versioning()
|
|
{
|
|
}
|
|
|
|
vint Versioning::AllocateVersion()
|
|
{
|
|
return ++version;
|
|
}
|
|
|
|
vint Versioning::GetVersion()
|
|
{
|
|
return version;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
.\WFLIBRARYREFLECTION.CPP
|
|
***********************************************************************/
|
|
#include <limits.h>
|
|
#include <float.h>
|
|
|
|
namespace vl
|
|
{
|
|
using namespace collections;
|
|
using namespace regex;
|
|
|
|
namespace reflection
|
|
{
|
|
namespace description
|
|
{
|
|
|
|
/***********************************************************************
|
|
TypeName
|
|
***********************************************************************/
|
|
|
|
#ifndef VCZH_DEBUG_NO_REFLECTION
|
|
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::Sys, system::Sys)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::Math, system::Math)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::Localization, system::Localization)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::CoroutineStatus, system::CoroutineStatus)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::CoroutineResult, system::CoroutineResult)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::ICoroutine, system::Coroutine)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::EnumerableCoroutine::IImpl, system::EnumerableCoroutine::IImpl)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::EnumerableCoroutine, system::EnumerableCoroutine)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::AsyncStatus, system::AsyncStatus)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::AsyncContext, system::AsyncContext)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::IAsync, system::Async)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::IPromise, system::Promise)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::IFuture, system::Future)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::IAsyncScheduler, system::AsyncScheduler)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::AsyncCoroutine::IImpl, system::AsyncCoroutine::IImpl)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::AsyncCoroutine, system::AsyncCoroutine)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::StateMachine, system::StateMachine)
|
|
IMPL_TYPE_INFO_RENAME(vl::reflection::description::Versioning, system::Versioning)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::RpcObjectReference, system::RpcObjectReference)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::RpcException, system::RpcException)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::RpcByvalReturnValue, system::RpcByvalReturnValue)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::IRpcSerializer, system::IRpcSerializer)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::IRpcJsonMessageDispatcher::RequestType, system::IRpcJsonMessageDispatcher::RequestType)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::IRpcJsonMessageDispatcher, system::IRpcJsonMessageDispatcher)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::IRpcListOps, system::IRpcListOps)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::IRpcListEventOps, system::IRpcListEventOps)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::IRpcObjectOps, system::IRpcObjectOps)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::IRpcObjectEventOps, system::IRpcObjectEventOps)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::IRpcOperations, system::IRpcOperations)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::IRpcDispatcher, system::IRpcDispatcher)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::IRpcController, system::IRpcController)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::IRpcLifecycle, system::IRpcLifecycle)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::IRpcWrapperBase, system::IRpcWrapperBase)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::RpcByrefEnumerator, system::RpcByrefEnumerator)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::RpcByrefEnumerable, system::RpcByrefEnumerable)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::RpcByrefReadonlyList, system::RpcByrefReadonlyList)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::RpcByrefList, system::RpcByrefList)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::RpcByrefArray, system::RpcByrefArray)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::RpcByrefObservableList, system::RpcByrefObservableList)
|
|
IMPL_TYPE_INFO_RENAME(vl::rpc_controller::RpcByrefDictionary, system::RpcByrefDictionary)
|
|
IMPL_TYPE_INFO_RENAME(vl::__vwsn::att_cpp_File, system::workflow_attributes::att_cpp_File)
|
|
IMPL_TYPE_INFO_RENAME(vl::__vwsn::att_cpp_UserImpl, system::workflow_attributes::att_cpp_UserImpl)
|
|
IMPL_TYPE_INFO_RENAME(vl::__vwsn::att_cpp_Private, system::workflow_attributes::att_cpp_Private)
|
|
IMPL_TYPE_INFO_RENAME(vl::__vwsn::att_cpp_Protected, system::workflow_attributes::att_cpp_Protected)
|
|
IMPL_TYPE_INFO_RENAME(vl::__vwsn::att_cpp_Friend, system::workflow_attributes::att_cpp_Friend)
|
|
IMPL_TYPE_INFO_RENAME(vl::__vwsn::att_rpc_Interface, system::workflow_attributes::att_rpc_Interface)
|
|
IMPL_TYPE_INFO_RENAME(vl::__vwsn::att_rpc_Ctor, system::workflow_attributes::att_rpc_Ctor)
|
|
IMPL_TYPE_INFO_RENAME(vl::__vwsn::att_rpc_Byval, system::workflow_attributes::att_rpc_Byval)
|
|
IMPL_TYPE_INFO_RENAME(vl::__vwsn::att_rpc_Byref, system::workflow_attributes::att_rpc_Byref)
|
|
IMPL_TYPE_INFO_RENAME(vl::__vwsn::att_rpc_Cached, system::workflow_attributes::att_rpc_Cached)
|
|
IMPL_TYPE_INFO_RENAME(vl::__vwsn::att_rpc_Dynamic, system::workflow_attributes::att_rpc_Dynamic)
|
|
IMPL_TYPE_INFO_RENAME(vl::__vwsn::att_rpc_IdString, system::workflow_attributes::att_rpc_IdString)
|
|
IMPL_TYPE_INFO_RENAME(vl::__vwsn::att_rpc_IdNumber, system::workflow_attributes::att_rpc_IdNumber)
|
|
|
|
#endif
|
|
|
|
/***********************************************************************
|
|
WfLoadLibraryTypes
|
|
***********************************************************************/
|
|
|
|
#ifdef VCZH_DESCRIPTABLEOBJECT_WITH_METADATA
|
|
|
|
#define _ ,
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::__vwsn::att_cpp_File)
|
|
STRUCT_MEMBER(argument)
|
|
END_STRUCT_MEMBER(vl::__vwsn::att_cpp_File)
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::__vwsn::att_cpp_UserImpl)
|
|
END_STRUCT_MEMBER(vl::__vwsn::att_cpp_UserImpl)
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::__vwsn::att_cpp_Private)
|
|
END_STRUCT_MEMBER(vl::__vwsn::att_cpp_Private)
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::__vwsn::att_cpp_Protected)
|
|
END_STRUCT_MEMBER(vl::__vwsn::att_cpp_Protected)
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::__vwsn::att_cpp_Friend)
|
|
STRUCT_MEMBER(argument)
|
|
END_STRUCT_MEMBER(vl::__vwsn::att_cpp_Friend)
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::__vwsn::att_rpc_Interface)
|
|
END_STRUCT_MEMBER(vl::__vwsn::att_rpc_Interface)
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::__vwsn::att_rpc_Ctor)
|
|
END_STRUCT_MEMBER(vl::__vwsn::att_rpc_Ctor)
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::__vwsn::att_rpc_Byval)
|
|
END_STRUCT_MEMBER(vl::__vwsn::att_rpc_Byval)
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::__vwsn::att_rpc_Byref)
|
|
END_STRUCT_MEMBER(vl::__vwsn::att_rpc_Byref)
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::__vwsn::att_rpc_Cached)
|
|
END_STRUCT_MEMBER(vl::__vwsn::att_rpc_Cached)
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::__vwsn::att_rpc_Dynamic)
|
|
END_STRUCT_MEMBER(vl::__vwsn::att_rpc_Dynamic)
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::__vwsn::att_rpc_IdString)
|
|
STRUCT_MEMBER(argument)
|
|
END_STRUCT_MEMBER(vl::__vwsn::att_rpc_IdString)
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::__vwsn::att_rpc_IdNumber)
|
|
STRUCT_MEMBER(argument)
|
|
END_STRUCT_MEMBER(vl::__vwsn::att_rpc_IdNumber)
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::rpc_controller::RpcObjectReference)
|
|
STRUCT_MEMBER(clientId)
|
|
STRUCT_MEMBER(objectId)
|
|
STRUCT_MEMBER(typeId)
|
|
END_STRUCT_MEMBER(vl::rpc_controller::RpcObjectReference)
|
|
|
|
BEGIN_STRUCT_MEMBER(vl::rpc_controller::RpcException)
|
|
STRUCT_MEMBER(message)
|
|
END_STRUCT_MEMBER(vl::rpc_controller::RpcException)
|
|
|
|
BEGIN_CLASS_MEMBER(vl::rpc_controller::RpcByvalReturnValue)
|
|
CLASS_MEMBER_CONSTRUCTOR(Ptr<vl::rpc_controller::RpcByvalReturnValue>(), NO_PARAMETER)
|
|
CLASS_MEMBER_FIELD(value)
|
|
CLASS_MEMBER_FIELD(slot)
|
|
END_CLASS_MEMBER(vl::rpc_controller::RpcByvalReturnValue)
|
|
|
|
BEGIN_INTERFACE_MEMBER(vl::rpc_controller::IRpcSerializer)
|
|
CLASS_MEMBER_METHOD(Serialize, { L"value" })
|
|
CLASS_MEMBER_METHOD(Deserialize, { L"value" })
|
|
END_INTERFACE_MEMBER(vl::rpc_controller::IRpcSerializer)
|
|
|
|
BEGIN_ENUM_ITEM(vl::rpc_controller::IRpcJsonMessageDispatcher::RequestType)
|
|
ENUM_CLASS_ITEM(Direct)
|
|
ENUM_CLASS_ITEM(Broadcast)
|
|
ENUM_CLASS_ITEM(BroadcastAndDrop)
|
|
END_ENUM_ITEM(vl::rpc_controller::IRpcJsonMessageDispatcher::RequestType)
|
|
|
|
BEGIN_INTERFACE_MEMBER(vl::rpc_controller::IRpcJsonMessageDispatcher)
|
|
CLASS_MEMBER_METHOD(AllocateRequestId, NO_PARAMETER)
|
|
CLASS_MEMBER_METHOD(OnJsonRequest, { L"message" _ L"requestType" })
|
|
END_INTERFACE_MEMBER(vl::rpc_controller::IRpcJsonMessageDispatcher)
|
|
|
|
BEGIN_INTERFACE_MEMBER(vl::rpc_controller::IRpcListOps)
|
|
CLASS_MEMBER_METHOD(EnumCreate, { L"ref" })
|
|
CLASS_MEMBER_METHOD(EnumNext, { L"enumerator" })
|
|
CLASS_MEMBER_METHOD(EnumGetCurrent, { L"enumerator" })
|
|
|
|
CLASS_MEMBER_METHOD(ListGetCount, { L"ref" })
|
|
CLASS_MEMBER_METHOD(ListGet, { L"ref" _ L"index" })
|
|
CLASS_MEMBER_METHOD(ListSet, { L"ref" _ L"index" _ L"value" })
|
|
CLASS_MEMBER_METHOD(ListAdd, { L"ref" _ L"value" })
|
|
CLASS_MEMBER_METHOD(ListInsert, { L"ref" _ L"index" _ L"value" })
|
|
CLASS_MEMBER_METHOD(ListRemoveAt, { L"ref" _ L"index" })
|
|
CLASS_MEMBER_METHOD(ListClear, { L"ref" })
|
|
CLASS_MEMBER_METHOD(ListContains, { L"ref" _ L"value" })
|
|
CLASS_MEMBER_METHOD(ListIndexOf, { L"ref" _ L"value" })
|
|
CLASS_MEMBER_METHOD(ArrayResize, { L"ref" _ L"size" })
|
|
|
|
CLASS_MEMBER_METHOD(DictGetCount, { L"ref" })
|
|
CLASS_MEMBER_METHOD(DictGet, { L"ref" _ L"key" })
|
|
CLASS_MEMBER_METHOD(DictSet, { L"ref" _ L"key" _ L"value" })
|
|
CLASS_MEMBER_METHOD(DictRemove, { L"ref" _ L"key" })
|
|
CLASS_MEMBER_METHOD(DictClear, { L"ref" })
|
|
CLASS_MEMBER_METHOD(DictContainsKey, { L"ref" _ L"key" })
|
|
CLASS_MEMBER_METHOD(DictGetKeys, { L"ref" })
|
|
CLASS_MEMBER_METHOD(DictGetValues, { L"ref" })
|
|
END_INTERFACE_MEMBER(vl::rpc_controller::IRpcListOps)
|
|
|
|
BEGIN_INTERFACE_MEMBER(vl::rpc_controller::IRpcListEventOps)
|
|
CLASS_MEMBER_METHOD(OnItemChanged, { L"ref" _ L"index" _ L"oldCount" _ L"newCount" })
|
|
END_INTERFACE_MEMBER(vl::rpc_controller::IRpcListEventOps)
|
|
|
|
BEGIN_INTERFACE_MEMBER(vl::rpc_controller::IRpcObjectOps)
|
|
CLASS_MEMBER_METHOD(InvokeMethod, { L"ref" _ L"methodId" _ L"arguments" })
|
|
CLASS_MEMBER_METHOD(EndInvokeMethod, { L"slot" })
|
|
CLASS_MEMBER_METHOD(ObjectHold, { L"ref" _ L"remoteClientId" _ L"hold" })
|
|
END_INTERFACE_MEMBER(vl::rpc_controller::IRpcObjectOps)
|
|
|
|
BEGIN_INTERFACE_MEMBER(vl::rpc_controller::IRpcObjectEventOps)
|
|
CLASS_MEMBER_METHOD(InvokeEvent, { L"ref" _ L"eventId" _ L"arguments" })
|
|
END_INTERFACE_MEMBER(vl::rpc_controller::IRpcObjectEventOps)
|
|
|
|
BEGIN_INTERFACE_MEMBER_NOPROXY(vl::rpc_controller::IRpcOperations)
|
|
CLASS_MEMBER_PROPERTY_READONLY_FAST(ObjectOps)
|
|
CLASS_MEMBER_PROPERTY_READONLY_FAST(ObjectEventOps)
|
|
END_INTERFACE_MEMBER(vl::rpc_controller::IRpcOperations)
|
|
|
|
BEGIN_INTERFACE_MEMBER_NOPROXY(vl::rpc_controller::IRpcDispatcher)
|
|
CLASS_MEMBER_METHOD(Finalize, NO_PARAMETER)
|
|
CLASS_MEMBER_METHOD(Initialize, NO_PARAMETER)
|
|
CLASS_MEMBER_METHOD(DeclareLocalService, { L"ref" })
|
|
CLASS_MEMBER_METHOD(BroadcastFromClient_ObjectEventOps, { L"selfClientId" })
|
|
CLASS_MEMBER_METHOD(SendToClient_ObjectOps, { L"targetClientId" })
|
|
END_INTERFACE_MEMBER(vl::rpc_controller::IRpcDispatcher)
|
|
|
|
BEGIN_INTERFACE_MEMBER_NOPROXY(vl::rpc_controller::IRpcController)
|
|
CLASS_MEMBER_BASE(vl::rpc_controller::IRpcOperations)
|
|
CLASS_MEMBER_METHOD(Finalize, NO_PARAMETER)
|
|
CLASS_MEMBER_METHOD(SetEventSuppressedFlag, { L"ref" _ L"eventId" _ L"suppressed" })
|
|
CLASS_MEMBER_METHOD(GetEventSuppressedFlag, { L"ref" _ L"eventId" })
|
|
CLASS_MEMBER_METHOD(SetItemChangedSuppressedFlag, { L"ref" _ L"suppressed" })
|
|
CLASS_MEMBER_METHOD(GetItemChangedSuppressedFlag, { L"ref" })
|
|
END_INTERFACE_MEMBER(vl::rpc_controller::IRpcController)
|
|
|
|
BEGIN_INTERFACE_MEMBER_NOPROXY(vl::rpc_controller::IRpcLifecycle)
|
|
CLASS_MEMBER_METHOD(Finalize, NO_PARAMETER)
|
|
CLASS_MEMBER_METHOD(Initialize, NO_PARAMETER)
|
|
CLASS_MEMBER_PROPERTY_READONLY_FAST(ClientId)
|
|
CLASS_MEMBER_PROPERTY_READONLY_FAST(Dispatcher)
|
|
CLASS_MEMBER_PROPERTY_READONLY_FAST(Controller)
|
|
CLASS_MEMBER_PROPERTY_READONLY_FAST(Serializer)
|
|
CLASS_MEMBER_METHOD(RefToPtr, { L"ref" })
|
|
CLASS_MEMBER_METHOD(PtrToRef, { L"obj" })
|
|
CLASS_MEMBER_METHOD(LocalObjectHold, { L"ref" _ L"remoteClientId" })
|
|
CLASS_MEMBER_METHOD(LocalObjectUnhold, { L"ref" _ L"remoteClientId" })
|
|
CLASS_MEMBER_METHOD(RegisterLocalService, { L"typeId" _ L"service" })
|
|
CLASS_MEMBER_METHOD(DeclareRemoteService, { L"ref" })
|
|
CLASS_MEMBER_METHOD(GetTypeIdFromName, { L"typeName" })
|
|
CLASS_MEMBER_METHOD(RequestService, { L"typeName" })
|
|
CLASS_MEMBER_STATIC_EXTERNALMETHOD(RpcBoxByref, { L"trivial" _ L"lc" }, vl::rpc_controller::RpcObjectReference(*)(vl::Ptr<vl::reflection::IDescriptable>, vl::rpc_controller::IRpcLifecycle*), vl::rpc_controller::RpcBoxByref)
|
|
CLASS_MEMBER_STATIC_EXTERNALMETHOD(RpcUnboxByref, { L"serializable" _ L"lc" }, vl::Ptr<vl::reflection::IDescriptable>(*)(vl::rpc_controller::RpcObjectReference, vl::rpc_controller::IRpcLifecycle*), vl::rpc_controller::RpcUnboxByref)
|
|
CLASS_MEMBER_STATIC_EXTERNALMETHOD(RpcCopyByval, { L"trivial" _ L"lc" }, Value(*)(const Value&, vl::rpc_controller::IRpcLifecycle*), vl::rpc_controller::RpcCopyByval)
|
|
CLASS_MEMBER_STATIC_EXTERNALMETHOD(RpcBoxByval, { L"trivial" _ L"lc" }, Value(*)(const Value&, vl::rpc_controller::IRpcLifecycle*), vl::rpc_controller::RpcBoxByval)
|
|
CLASS_MEMBER_STATIC_EXTERNALMETHOD(RpcUnboxByval, { L"serializable" _ L"lc" }, vl::Ptr<vl::reflection::IDescriptable>(*)(const Value&, vl::rpc_controller::IRpcLifecycle*), vl::rpc_controller::RpcUnboxByval)
|
|
CLASS_MEMBER_STATIC_EXTERNALMETHOD(ReadMethodException, { L"value" }, void(*)(const Value&), vl::rpc_controller::ReadMethodException)
|
|
CLASS_MEMBER_STATIC_EXTERNALMETHOD(ReadEventException, { L"exceptions" }, void(*)(vl::rpc_controller::RpcEventExceptionMap), vl::rpc_controller::ReadEventException)
|
|
CLASS_MEMBER_STATIC_EXTERNALMETHOD(JsonSerializePredefinedTypes, { L"value" _ L"rpcjson_Serialize" }, vl::Ptr<vl::glr::json::JsonNode>(*)(const Value&, const vl::rpc_controller::RpcJsonSerializeCallback&), vl::rpc_controller::JsonSerializePredefinedTypes)
|
|
CLASS_MEMBER_STATIC_EXTERNALMETHOD(JsonDeserializePredefinedTypes, { L"value" _ L"rpcjson_Deserialize" }, Value(*)(const Value&, const vl::rpc_controller::RpcJsonDeserializeCallback&), vl::rpc_controller::JsonDeserializePredefinedTypes)
|
|
END_INTERFACE_MEMBER(vl::rpc_controller::IRpcLifecycle)
|
|
|
|
BEGIN_INTERFACE_MEMBER(vl::rpc_controller::IRpcWrapperBase)
|
|
CLASS_MEMBER_METHOD(DisconnectFromLifecycle, NO_PARAMETER)
|
|
END_INTERFACE_MEMBER(vl::rpc_controller::IRpcWrapperBase)
|
|
|
|
BEGIN_CLASS_MEMBER(vl::rpc_controller::RpcByrefEnumerator)
|
|
CLASS_MEMBER_BASE(vl::reflection::description::IValueEnumerator)
|
|
CLASS_MEMBER_BASE(vl::rpc_controller::IRpcWrapperBase)
|
|
END_CLASS_MEMBER(vl::rpc_controller::RpcByrefEnumerator)
|
|
|
|
BEGIN_CLASS_MEMBER(vl::rpc_controller::RpcByrefEnumerable)
|
|
CLASS_MEMBER_BASE(vl::reflection::description::IValueEnumerable)
|
|
CLASS_MEMBER_BASE(vl::rpc_controller::IRpcWrapperBase)
|
|
END_CLASS_MEMBER(vl::rpc_controller::RpcByrefEnumerable)
|
|
|
|
BEGIN_CLASS_MEMBER(vl::rpc_controller::RpcByrefReadonlyList)
|
|
CLASS_MEMBER_BASE(vl::reflection::description::IValueReadonlyList)
|
|
CLASS_MEMBER_BASE(vl::rpc_controller::IRpcWrapperBase)
|
|
END_CLASS_MEMBER(vl::rpc_controller::RpcByrefReadonlyList)
|
|
|
|
BEGIN_CLASS_MEMBER(vl::rpc_controller::RpcByrefList)
|
|
CLASS_MEMBER_BASE(vl::rpc_controller::RpcByrefReadonlyList)
|
|
CLASS_MEMBER_BASE(vl::reflection::description::IValueList)
|
|
END_CLASS_MEMBER(vl::rpc_controller::RpcByrefList)
|
|
|
|
BEGIN_CLASS_MEMBER(vl::rpc_controller::RpcByrefArray)
|
|
CLASS_MEMBER_BASE(vl::reflection::description::IValueArray)
|
|
CLASS_MEMBER_BASE(vl::rpc_controller::IRpcWrapperBase)
|
|
END_CLASS_MEMBER(vl::rpc_controller::RpcByrefArray)
|
|
|
|
BEGIN_CLASS_MEMBER(vl::rpc_controller::RpcByrefObservableList)
|
|
CLASS_MEMBER_BASE(vl::reflection::description::IValueObservableList)
|
|
CLASS_MEMBER_BASE(vl::rpc_controller::IRpcWrapperBase)
|
|
END_CLASS_MEMBER(vl::rpc_controller::RpcByrefObservableList)
|
|
|
|
BEGIN_CLASS_MEMBER(vl::rpc_controller::RpcByrefDictionary)
|
|
CLASS_MEMBER_BASE(vl::reflection::description::IValueDictionary)
|
|
CLASS_MEMBER_BASE(vl::rpc_controller::IRpcWrapperBase)
|
|
END_CLASS_MEMBER(vl::rpc_controller::RpcByrefDictionary)
|
|
|
|
BEGIN_CLASS_MEMBER(Sys)
|
|
CLASS_MEMBER_STATIC_METHOD(Int32ToInt, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(Int64ToInt, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(IntToInt32, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(IntToInt64, { L"value" })
|
|
|
|
CLASS_MEMBER_STATIC_METHOD(UInt32ToUInt, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(UInt64ToUInt, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(UIntToUInt32, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(UIntToUInt64, { L"value" })
|
|
|
|
CLASS_MEMBER_STATIC_METHOD(Len, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(Left, { L"value" _ L"length" })
|
|
CLASS_MEMBER_STATIC_METHOD(Right, { L"value" _ L"length" })
|
|
CLASS_MEMBER_STATIC_METHOD(Mid, { L"value" _ L"start" _ L"length" })
|
|
CLASS_MEMBER_STATIC_METHOD(Find, { L"value" _ L"substr" })
|
|
CLASS_MEMBER_STATIC_METHOD(UCase, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(LCase, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(LoremIpsumTitle, { L"bestLength" })
|
|
CLASS_MEMBER_STATIC_METHOD(LoremIpsumSentence, { L"bestLength" })
|
|
CLASS_MEMBER_STATIC_METHOD(LoremIpsumParagraph, { L"bestLength" })
|
|
CLASS_MEMBER_STATIC_METHOD(ReverseEnumerable, { L"value" })
|
|
#pragma push_macro("CompareString")
|
|
#if defined CompareString
|
|
#undef CompareString
|
|
#endif
|
|
#define DEFINE_COMPARE(TYPE) CLASS_MEMBER_STATIC_METHOD_OVERLOAD(Compare, PROTECT_PARAMETERS({L"a" _ L"b"}), vint(*)(TYPE, TYPE))
|
|
REFLECTION_PREDEFINED_PRIMITIVE_TYPES(DEFINE_COMPARE)
|
|
DEFINE_COMPARE(DateTime)
|
|
#undef DEFINE_COMPARE
|
|
#pragma pop_macro("CompareString")
|
|
|
|
CLASS_MEMBER_STATIC_METHOD(GetLocalTime, NO_PARAMETER)
|
|
CLASS_MEMBER_STATIC_METHOD(GetUtcTime, NO_PARAMETER)
|
|
CLASS_MEMBER_STATIC_METHOD(ToLocalTime, { L"dt" })
|
|
CLASS_MEMBER_STATIC_METHOD(ToUtcTime, { L"dt" })
|
|
CLASS_MEMBER_STATIC_METHOD(Forward, { L"dt" _ L"milliseconds" })
|
|
CLASS_MEMBER_STATIC_METHOD(Backward, { L"dt" _ L"milliseconds" })
|
|
|
|
CLASS_MEMBER_STATIC_METHOD_OVERLOAD(CreateDateTime, {L"year" _ L"month" _ L"day" }, DateTime(*)(vint, vint, vint))
|
|
CLASS_MEMBER_STATIC_METHOD_OVERLOAD(CreateDateTime, { L"year" _ L"month" _ L"day" _ L"hour" _ L"minute" _ L"second" _ L"milliseconds" }, DateTime(*)(vint, vint, vint, vint, vint, vint, vint))
|
|
END_CLASS_MEMBER(Sys)
|
|
|
|
BEGIN_CLASS_MEMBER(Math)
|
|
CLASS_MEMBER_STATIC_METHOD(Pi, NO_PARAMETER)
|
|
|
|
CLASS_MEMBER_STATIC_METHOD_OVERLOAD(Abs, { L"value" }, vint8_t(*)(vint8_t))
|
|
CLASS_MEMBER_STATIC_METHOD_OVERLOAD(Abs, { L"value" }, vint16_t(*)(vint16_t))
|
|
CLASS_MEMBER_STATIC_METHOD_OVERLOAD(Abs, { L"value" }, vint32_t(*)(vint32_t))
|
|
CLASS_MEMBER_STATIC_METHOD_OVERLOAD(Abs, { L"value" }, vint64_t(*)(vint64_t))
|
|
CLASS_MEMBER_STATIC_METHOD_OVERLOAD(Abs, { L"value" }, float(*)(float))
|
|
CLASS_MEMBER_STATIC_METHOD_OVERLOAD(Abs, { L"value" }, double(*)(double))
|
|
|
|
#define DEFINE_MINMAX(TYPE)\
|
|
CLASS_MEMBER_STATIC_METHOD_OVERLOAD(Min, PROTECT_PARAMETERS({L"a" _ L"b"}), TYPE(*)(TYPE, TYPE))\
|
|
CLASS_MEMBER_STATIC_METHOD_OVERLOAD(Max, PROTECT_PARAMETERS({L"a" _ L"b"}), TYPE(*)(TYPE, TYPE))\
|
|
|
|
REFLECTION_PREDEFINED_PRIMITIVE_TYPES(DEFINE_MINMAX)
|
|
DEFINE_MINMAX(DateTime)
|
|
#undef DEFINE_MINMAX
|
|
|
|
CLASS_MEMBER_STATIC_METHOD(Sin, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(Cos, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(Tan, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(ASin, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(ACos, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(ATan, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(ATan2, { L"x" _ L"y" })
|
|
|
|
CLASS_MEMBER_STATIC_METHOD(Exp, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(LogN, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(Log10, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(Log, { L"value" _ L"base" })
|
|
CLASS_MEMBER_STATIC_METHOD(Pow, { L"value" _ L"power" })
|
|
CLASS_MEMBER_STATIC_METHOD(Ceil, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(Floor, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(Round, { L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(Trunc, { L"value" })
|
|
END_CLASS_MEMBER(Math)
|
|
|
|
BEGIN_CLASS_MEMBER(Localization)
|
|
CLASS_MEMBER_STATIC_METHOD(Invariant, NO_PARAMETER)
|
|
CLASS_MEMBER_STATIC_METHOD(System, NO_PARAMETER)
|
|
CLASS_MEMBER_STATIC_METHOD(User, NO_PARAMETER)
|
|
CLASS_MEMBER_STATIC_METHOD(Locales, NO_PARAMETER)
|
|
|
|
CLASS_MEMBER_STATIC_METHOD(GetShortDateFormats, { L"locale" })
|
|
CLASS_MEMBER_STATIC_METHOD(GetLongDateFormats, { L"locale" })
|
|
CLASS_MEMBER_STATIC_METHOD(GetYearMonthDateFormats, { L"locale" })
|
|
CLASS_MEMBER_STATIC_METHOD(GetLongTimeFormats, { L"locale" })
|
|
CLASS_MEMBER_STATIC_METHOD(GetShortTimeFormats, { L"locale" })
|
|
|
|
CLASS_MEMBER_STATIC_METHOD(GetShortDayOfWeekName, { L"locale" _ L"dayOfWeek" })
|
|
CLASS_MEMBER_STATIC_METHOD(GetLongDayOfWeekName, { L"locale" _ L"dayOfWeek" })
|
|
CLASS_MEMBER_STATIC_METHOD(GetShortMonthName, { L"locale" _ L"month" })
|
|
CLASS_MEMBER_STATIC_METHOD(GetLongMonthName, { L"locale" _ L"month" })
|
|
|
|
CLASS_MEMBER_STATIC_METHOD(FormatDate, { L"locale" _ L"format" _ L"date" })
|
|
CLASS_MEMBER_STATIC_METHOD(FormatTime, { L"locale" _ L"format" _ L"date" })
|
|
CLASS_MEMBER_STATIC_METHOD(FormatNumber, { L"locale" _ L"number" })
|
|
CLASS_MEMBER_STATIC_METHOD(FormatCurrency, { L"locale" _ L"number" })
|
|
END_CLASS_MEMBER(Localization)
|
|
|
|
BEGIN_ENUM_ITEM(CoroutineStatus)
|
|
ENUM_CLASS_ITEM(Waiting)
|
|
ENUM_CLASS_ITEM(Executing)
|
|
ENUM_CLASS_ITEM(Stopped)
|
|
END_ENUM_ITEM(CoroutineStatus)
|
|
|
|
BEGIN_INTERFACE_MEMBER(ICoroutine)
|
|
CLASS_MEMBER_METHOD(Resume, { L"raiseException" _ L"output" })
|
|
CLASS_MEMBER_PROPERTY_READONLY_FAST(Failure)
|
|
CLASS_MEMBER_PROPERTY_READONLY_FAST(Status)
|
|
END_INTERFACE_MEMBER(ICoroutine)
|
|
|
|
BEGIN_CLASS_MEMBER(CoroutineResult)
|
|
CLASS_MEMBER_CONSTRUCTOR(Ptr<CoroutineResult>(), NO_PARAMETER)
|
|
CLASS_MEMBER_PROPERTY_FAST(Result)
|
|
CLASS_MEMBER_PROPERTY_FAST(Failure)
|
|
END_CLASS_MEMBER(CoroutineResult)
|
|
|
|
BEGIN_INTERFACE_MEMBER_NOPROXY(EnumerableCoroutine::IImpl)
|
|
CLASS_MEMBER_BASE(IValueEnumerator)
|
|
END_INTERFACE_MEMBER(EnumerableCoroutine::IImpl)
|
|
|
|
BEGIN_CLASS_MEMBER(EnumerableCoroutine)
|
|
CLASS_MEMBER_STATIC_METHOD(YieldAndPause, { L"impl" _ L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(JoinAndPause, { L"impl" _ L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(ReturnAndExit, { L"impl" })
|
|
CLASS_MEMBER_STATIC_METHOD(Create, { L"creator" })
|
|
END_CLASS_MEMBER(EnumerableCoroutine)
|
|
|
|
BEGIN_ENUM_ITEM(AsyncStatus)
|
|
ENUM_CLASS_ITEM(Ready)
|
|
ENUM_CLASS_ITEM(Executing)
|
|
ENUM_CLASS_ITEM(Stopped)
|
|
END_ENUM_ITEM(AsyncStatus)
|
|
|
|
BEGIN_CLASS_MEMBER(AsyncContext)
|
|
CLASS_MEMBER_CONSTRUCTOR(Ptr<AsyncContext>(const Value&), {L"context"})
|
|
CLASS_MEMBER_METHOD(IsCancelled, NO_PARAMETER)
|
|
CLASS_MEMBER_METHOD(Cancel, NO_PARAMETER)
|
|
CLASS_MEMBER_PROPERTY_FAST(Context)
|
|
END_CLASS_MEMBER(AsyncContext)
|
|
|
|
BEGIN_INTERFACE_MEMBER(IAsync)
|
|
CLASS_MEMBER_PROPERTY_READONLY_FAST(Status)
|
|
CLASS_MEMBER_METHOD(Execute, { L"callback" _ L"context" })
|
|
CLASS_MEMBER_STATIC_METHOD(Delay, { L"milliseconds" })
|
|
END_INTERFACE_MEMBER(IAsync)
|
|
|
|
BEGIN_INTERFACE_MEMBER_NOPROXY(IPromise)
|
|
CLASS_MEMBER_METHOD(SendResult, { L"result" })
|
|
CLASS_MEMBER_METHOD(SendFailure, { L"failure" })
|
|
END_INTERFACE_MEMBER(IPromise)
|
|
|
|
BEGIN_INTERFACE_MEMBER_NOPROXY(IFuture)
|
|
CLASS_MEMBER_BASE(IAsync)
|
|
CLASS_MEMBER_BASE(IPromise)
|
|
CLASS_MEMBER_PROPERTY_READONLY_FAST(Promise)
|
|
CLASS_MEMBER_STATIC_METHOD(Create, NO_PARAMETER)
|
|
END_INTERFACE_MEMBER(IFuture)
|
|
|
|
BEGIN_INTERFACE_MEMBER_NOPROXY(IAsyncScheduler)
|
|
CLASS_MEMBER_METHOD(Execute, { L"callback" })
|
|
CLASS_MEMBER_METHOD(ExecuteInBackground, { L"callback" })
|
|
CLASS_MEMBER_METHOD(DelayExecute, { L"callback" _ L"milliseconds" })
|
|
CLASS_MEMBER_STATIC_METHOD(GetSchedulerForCurrentThread, NO_PARAMETER)
|
|
END_INTERFACE_MEMBER(IAsyncScheduler)
|
|
|
|
BEGIN_INTERFACE_MEMBER_NOPROXY(AsyncCoroutine::IImpl)
|
|
CLASS_MEMBER_BASE(IAsync)
|
|
END_INTERFACE_MEMBER(AsyncCoroutine::IImpl)
|
|
|
|
BEGIN_CLASS_MEMBER(AsyncCoroutine)
|
|
CLASS_MEMBER_STATIC_METHOD(AwaitAndRead, { L"impl" _ L"value" })
|
|
CLASS_MEMBER_STATIC_METHOD(ReturnAndExit, { L"impl" _ L"value"})
|
|
CLASS_MEMBER_STATIC_METHOD(QueryContext, { L"impl" })
|
|
CLASS_MEMBER_STATIC_METHOD(Create, { L"creator" })
|
|
CLASS_MEMBER_STATIC_METHOD(CreateAndRun, { L"creator" })
|
|
END_CLASS_MEMBER(AsyncCoroutine)
|
|
|
|
BEGIN_CLASS_MEMBER(StateMachine)
|
|
CLASS_MEMBER_FIELD(stateMachineInitialized)
|
|
CLASS_MEMBER_FIELD(stateMachineStopped)
|
|
CLASS_MEMBER_FIELD(stateMachineInput)
|
|
CLASS_MEMBER_FIELD(stateMachineCoroutine)
|
|
|
|
CLASS_MEMBER_CONSTRUCTOR(Ptr<StateMachine>(), NO_PARAMETER)
|
|
CLASS_MEMBER_METHOD(ResumeStateMachine, NO_PARAMETER)
|
|
|
|
CLASS_MEMBER_PROPERTY_READONLY_FAST(StateMachineStatus)
|
|
END_CLASS_MEMBER(StateMachine)
|
|
|
|
BEGIN_CLASS_MEMBER(Versioning)
|
|
CLASS_MEMBER_CONSTRUCTOR(Ptr<Versioning>(), NO_PARAMETER)
|
|
CLASS_MEMBER_METHOD(AllocateVersion, NO_PARAMETER)
|
|
CLASS_MEMBER_PROPERTY_READONLY_FAST(Version)
|
|
END_CLASS_MEMBER(Versioning)
|
|
#undef _
|
|
|
|
class WfLibraryTypeLoader : public Object, public ITypeLoader
|
|
{
|
|
public:
|
|
void Load(ITypeManager* manager)override
|
|
{
|
|
WORKFLOW_LIBRARY_TYPES(ADD_TYPE_INFO)
|
|
}
|
|
|
|
void Unload(ITypeManager* manager)override
|
|
{
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
bool WfLoadLibraryTypes()
|
|
{
|
|
#ifdef VCZH_DESCRIPTABLEOBJECT_WITH_METADATA
|
|
ITypeManager* manager = GetGlobalTypeManager();
|
|
if (manager)
|
|
{
|
|
auto loader = Ptr(new WfLibraryTypeLoader);
|
|
return manager->AddTypeLoader(loader);
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
.\RPC\WFLIBRARYRPC.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace rpc_controller
|
|
{
|
|
using namespace collections;
|
|
using namespace reflection;
|
|
using namespace reflection::description;
|
|
|
|
namespace
|
|
{
|
|
#ifndef VCZH_WORKFLOW_RPC_OBJECT_REFERENCE_VALUE_HELPERS
|
|
#define VCZH_WORKFLOW_RPC_OBJECT_REFERENCE_VALUE_HELPERS
|
|
bool IsRpcObjectReferenceValue(const Value& value)
|
|
{
|
|
return value.GetValueType() == Value::BoxedValue && value.GetBoxedValue().Cast<IValueType::TypedBox<RpcObjectReference>>();
|
|
}
|
|
|
|
RpcObjectReference GetRpcObjectReference(const Value& value)
|
|
{
|
|
auto boxed = value.GetBoxedValue().Cast<IValueType::TypedBox<RpcObjectReference>>();
|
|
CHECK_ERROR(boxed, L"RpcObjectReference is expected.");
|
|
return boxed->value;
|
|
}
|
|
#endif
|
|
|
|
bool IsNullRpcObjectReference(RpcObjectReference ref)
|
|
{
|
|
return ref.clientId == RpcClientId_Invalid
|
|
&& ref.objectId == RpcObjectId_Invalid
|
|
&& ref.typeId == RpcTypeId_NotFound;
|
|
}
|
|
|
|
template<typename K, typename V>
|
|
bool ContainsKey(const Dictionary<K, V>& xs, const K& key)
|
|
{
|
|
return xs.Keys().Contains(key);
|
|
}
|
|
|
|
template<typename TInterface>
|
|
Ptr<TInterface> TryGetValueInterface(const Value& value)
|
|
{
|
|
if (auto raw = value.GetRawPtr())
|
|
{
|
|
return Ptr(dynamic_cast<TInterface*>(raw));
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Value RpcCopyValueByvalInternal(const Value& trivial, Dictionary<const DescriptableObject*, bool>& visited)
|
|
{
|
|
if (trivial.IsNull()) return trivial;
|
|
if (trivial.GetValueType() == Value::SharedPtr)
|
|
{
|
|
if (auto roDict = TryGetValueInterface<IValueReadonlyDictionary>(trivial))
|
|
{
|
|
auto key = static_cast<const DescriptableObject*>(trivial.GetRawPtr());
|
|
if (ContainsKey(visited, key)) CHECK_FAIL(L"Byval copying does not support cycles.");
|
|
visited.Add(key, true);
|
|
|
|
auto dict = IValueDictionary::Create();
|
|
auto keys = roDict->GetKeys();
|
|
for (vint i = 0; i < keys->GetCount(); i++)
|
|
{
|
|
auto dictKey = keys->Get(i);
|
|
dict->Set(
|
|
RpcCopyValueByvalInternal(dictKey, visited),
|
|
RpcCopyValueByvalInternal(roDict->Get(dictKey), visited)
|
|
);
|
|
}
|
|
visited.Remove(key);
|
|
return BoxValue(dict);
|
|
}
|
|
|
|
if (auto obsList = TryGetValueInterface<IValueObservableList>(trivial))
|
|
{
|
|
auto key = static_cast<const DescriptableObject*>(trivial.GetRawPtr());
|
|
if (ContainsKey(visited, key)) CHECK_FAIL(L"Byval copying does not support cycles.");
|
|
visited.Add(key, true);
|
|
|
|
auto list = IValueObservableList::Create();
|
|
for (vint i = 0; i < obsList->GetCount(); i++)
|
|
{
|
|
list->Add(RpcCopyValueByvalInternal(obsList->Get(i), visited));
|
|
}
|
|
visited.Remove(key);
|
|
return BoxValue(list);
|
|
}
|
|
|
|
if (auto array = TryGetValueInterface<IValueArray>(trivial))
|
|
{
|
|
auto key = static_cast<const DescriptableObject*>(trivial.GetRawPtr());
|
|
if (ContainsKey(visited, key)) CHECK_FAIL(L"Byval copying does not support cycles.");
|
|
visited.Add(key, true);
|
|
|
|
auto list = IValueArray::Create();
|
|
list->Resize(array->GetCount());
|
|
for (vint i = 0; i < array->GetCount(); i++)
|
|
{
|
|
list->Set(i, RpcCopyValueByvalInternal(array->Get(i), visited));
|
|
}
|
|
visited.Remove(key);
|
|
return BoxValue(list);
|
|
}
|
|
|
|
if (auto roList = TryGetValueInterface<IValueReadonlyList>(trivial))
|
|
{
|
|
auto key = static_cast<const DescriptableObject*>(trivial.GetRawPtr());
|
|
if (ContainsKey(visited, key)) CHECK_FAIL(L"Byval copying does not support cycles.");
|
|
visited.Add(key, true);
|
|
|
|
auto list = IValueList::Create();
|
|
for (vint i = 0; i < roList->GetCount(); i++)
|
|
{
|
|
list->Add(RpcCopyValueByvalInternal(roList->Get(i), visited));
|
|
}
|
|
visited.Remove(key);
|
|
return BoxValue(list);
|
|
}
|
|
}
|
|
return trivial;
|
|
}
|
|
|
|
Value RpcBoxValueByvalInternal(const Value& trivial, IRpcLifecycle* lc, Dictionary<const DescriptableObject*, bool>& visited)
|
|
{
|
|
if (trivial.IsNull()) return trivial;
|
|
if (trivial.GetValueType() == Value::SharedPtr)
|
|
{
|
|
if (auto roDict = TryGetValueInterface<IValueReadonlyDictionary>(trivial))
|
|
{
|
|
auto key = static_cast<const DescriptableObject*>(trivial.GetRawPtr());
|
|
if (ContainsKey(visited, key)) CHECK_FAIL(L"Byval boxing does not support cycles.");
|
|
visited.Add(key, true);
|
|
|
|
auto dict = IValueDictionary::Create();
|
|
auto keys = roDict->GetKeys();
|
|
for (vint i = 0; i < keys->GetCount(); i++)
|
|
{
|
|
auto dictKey = keys->Get(i);
|
|
dict->Set(
|
|
RpcBoxValueByvalInternal(dictKey, lc, visited),
|
|
RpcBoxValueByvalInternal(roDict->Get(dictKey), lc, visited)
|
|
);
|
|
}
|
|
visited.Remove(key);
|
|
return BoxValue(dict);
|
|
}
|
|
|
|
if (auto obsList = TryGetValueInterface<IValueObservableList>(trivial))
|
|
{
|
|
auto key = static_cast<const DescriptableObject*>(trivial.GetRawPtr());
|
|
if (ContainsKey(visited, key)) CHECK_FAIL(L"Byval boxing does not support cycles.");
|
|
visited.Add(key, true);
|
|
|
|
auto list = IValueObservableList::Create();
|
|
for (vint i = 0; i < obsList->GetCount(); i++)
|
|
{
|
|
list->Add(RpcBoxValueByvalInternal(obsList->Get(i), lc, visited));
|
|
}
|
|
visited.Remove(key);
|
|
return BoxValue(list);
|
|
}
|
|
|
|
if (auto array = TryGetValueInterface<IValueArray>(trivial))
|
|
{
|
|
auto key = static_cast<const DescriptableObject*>(trivial.GetRawPtr());
|
|
if (ContainsKey(visited, key)) CHECK_FAIL(L"Byval boxing does not support cycles.");
|
|
visited.Add(key, true);
|
|
|
|
auto list = IValueArray::Create();
|
|
list->Resize(array->GetCount());
|
|
for (vint i = 0; i < array->GetCount(); i++)
|
|
{
|
|
list->Set(i, RpcBoxValueByvalInternal(array->Get(i), lc, visited));
|
|
}
|
|
visited.Remove(key);
|
|
return BoxValue(list);
|
|
}
|
|
|
|
if (auto roList = TryGetValueInterface<IValueReadonlyList>(trivial))
|
|
{
|
|
auto key = static_cast<const DescriptableObject*>(trivial.GetRawPtr());
|
|
if (ContainsKey(visited, key)) CHECK_FAIL(L"Byval boxing does not support cycles.");
|
|
visited.Add(key, true);
|
|
|
|
auto list = IValueList::Create();
|
|
for (vint i = 0; i < roList->GetCount(); i++)
|
|
{
|
|
list->Add(RpcBoxValueByvalInternal(roList->Get(i), lc, visited));
|
|
}
|
|
visited.Remove(key);
|
|
return BoxValue(list);
|
|
}
|
|
|
|
if (auto raw = trivial.GetRawPtr())
|
|
{
|
|
if (auto obj = dynamic_cast<IDescriptable*>(raw))
|
|
{
|
|
auto ref = lc->PtrToRef(Ptr<IDescriptable>(obj));
|
|
return BoxValue(ref);
|
|
}
|
|
}
|
|
}
|
|
return trivial;
|
|
}
|
|
|
|
Value RpcUnboxValueByvalInternal(const Value& serializable, IRpcLifecycle* lc, Dictionary<const DescriptableObject*, bool>& visited)
|
|
{
|
|
if (serializable.IsNull()) return serializable;
|
|
|
|
if (IsRpcObjectReferenceValue(serializable))
|
|
{
|
|
auto ref = GetRpcObjectReference(serializable);
|
|
auto obj = RpcUnboxByref(ref, lc);
|
|
return BoxValue(obj);
|
|
}
|
|
|
|
if (serializable.GetValueType() == Value::SharedPtr)
|
|
{
|
|
if (auto roDict = TryGetValueInterface<IValueReadonlyDictionary>(serializable))
|
|
{
|
|
auto key = static_cast<const DescriptableObject*>(serializable.GetRawPtr());
|
|
if (ContainsKey(visited, key)) CHECK_FAIL(L"Byval unboxing does not support cycles.");
|
|
visited.Add(key, true);
|
|
|
|
auto dict = IValueDictionary::Create();
|
|
auto keys = roDict->GetKeys();
|
|
for (vint i = 0; i < keys->GetCount(); i++)
|
|
{
|
|
auto dictKey = keys->Get(i);
|
|
dict->Set(
|
|
RpcUnboxValueByvalInternal(dictKey, lc, visited),
|
|
RpcUnboxValueByvalInternal(roDict->Get(dictKey), lc, visited)
|
|
);
|
|
}
|
|
visited.Remove(key);
|
|
return BoxValue(dict);
|
|
}
|
|
|
|
if (auto obsList = TryGetValueInterface<IValueObservableList>(serializable))
|
|
{
|
|
auto key = static_cast<const DescriptableObject*>(serializable.GetRawPtr());
|
|
if (ContainsKey(visited, key)) CHECK_FAIL(L"Byval unboxing does not support cycles.");
|
|
visited.Add(key, true);
|
|
|
|
auto list = IValueObservableList::Create();
|
|
for (vint i = 0; i < obsList->GetCount(); i++)
|
|
{
|
|
list->Add(RpcUnboxValueByvalInternal(obsList->Get(i), lc, visited));
|
|
}
|
|
visited.Remove(key);
|
|
return BoxValue(list);
|
|
}
|
|
|
|
if (auto array = TryGetValueInterface<IValueArray>(serializable))
|
|
{
|
|
auto key = static_cast<const DescriptableObject*>(serializable.GetRawPtr());
|
|
if (ContainsKey(visited, key)) CHECK_FAIL(L"Byval unboxing does not support cycles.");
|
|
visited.Add(key, true);
|
|
|
|
auto list = IValueArray::Create();
|
|
list->Resize(array->GetCount());
|
|
for (vint i = 0; i < array->GetCount(); i++)
|
|
{
|
|
list->Set(i, RpcUnboxValueByvalInternal(array->Get(i), lc, visited));
|
|
}
|
|
visited.Remove(key);
|
|
return BoxValue(list);
|
|
}
|
|
|
|
if (auto roList = TryGetValueInterface<IValueReadonlyList>(serializable))
|
|
{
|
|
auto key = static_cast<const DescriptableObject*>(serializable.GetRawPtr());
|
|
if (ContainsKey(visited, key)) CHECK_FAIL(L"Byval unboxing does not support cycles.");
|
|
visited.Add(key, true);
|
|
|
|
auto list = IValueList::Create();
|
|
for (vint i = 0; i < roList->GetCount(); i++)
|
|
{
|
|
list->Add(RpcUnboxValueByvalInternal(roList->Get(i), lc, visited));
|
|
}
|
|
visited.Remove(key);
|
|
return BoxValue(list);
|
|
}
|
|
}
|
|
return serializable;
|
|
}
|
|
}
|
|
|
|
void MergeRpcEventExceptionMap(RpcEventExceptionMap target, RpcEventExceptionMap source)
|
|
{
|
|
if (!source) return;
|
|
auto keys = source->GetKeys();
|
|
for (vint i = 0; i < keys->GetCount(); i++)
|
|
{
|
|
auto key = keys->Get(i);
|
|
target->Set(key, source->Get(key));
|
|
}
|
|
}
|
|
|
|
RpcObjectReference RpcBoxByref(Ptr<IDescriptable> trivial, IRpcLifecycle* lc)
|
|
{
|
|
if (!lc) CHECK_FAIL(L"IRpcLifecycle cannot be null.");
|
|
if (!trivial) return {};
|
|
return lc->PtrToRef(trivial);
|
|
}
|
|
|
|
Ptr<IDescriptable> RpcUnboxByref(RpcObjectReference serializable, IRpcLifecycle* lc)
|
|
{
|
|
if (!lc) CHECK_FAIL(L"IRpcLifecycle cannot be null.");
|
|
if (IsNullRpcObjectReference(serializable)) return nullptr;
|
|
return lc->RefToPtr(serializable);
|
|
}
|
|
|
|
Value RpcCopyByval(const Value& trivial, IRpcLifecycle* lc)
|
|
{
|
|
if (!lc) CHECK_FAIL(L"IRpcLifecycle cannot be null.");
|
|
if (trivial.IsNull()) return {};
|
|
Dictionary<const DescriptableObject*, bool> visited;
|
|
return RpcCopyValueByvalInternal(trivial, visited);
|
|
}
|
|
|
|
Value RpcBoxByval(Ptr<IDescriptable> trivial, IRpcLifecycle* lc)
|
|
{
|
|
if (!lc) CHECK_FAIL(L"IRpcLifecycle cannot be null.");
|
|
return RpcBoxByval(BoxValue(trivial), lc);
|
|
}
|
|
|
|
Value RpcBoxByval(const Value& trivial, IRpcLifecycle* lc)
|
|
{
|
|
if (!lc) CHECK_FAIL(L"IRpcLifecycle cannot be null.");
|
|
if (trivial.IsNull()) return {};
|
|
Dictionary<const DescriptableObject*, bool> visited;
|
|
return RpcBoxValueByvalInternal(trivial, lc, visited);
|
|
}
|
|
|
|
Ptr<IDescriptable> RpcUnboxByval(const Value& serializable, IRpcLifecycle* lc)
|
|
{
|
|
if (!lc) CHECK_FAIL(L"IRpcLifecycle cannot be null.");
|
|
if (serializable.IsNull()) return nullptr;
|
|
Dictionary<const DescriptableObject*, bool> visited;
|
|
auto trivial = RpcUnboxValueByvalInternal(serializable, lc, visited);
|
|
if (auto raw = trivial.GetRawPtr())
|
|
{
|
|
if (auto obj = dynamic_cast<IDescriptable*>(raw))
|
|
{
|
|
return Ptr<IDescriptable>(obj);
|
|
}
|
|
}
|
|
|
|
CHECK_FAIL(L"Interface value or null is expected.");
|
|
return nullptr;
|
|
}
|
|
|
|
void ReadMethodException(const Value& value)
|
|
{
|
|
if (value.GetValueType() == Value::BoxedValue)
|
|
{
|
|
if (auto boxed = value.GetBoxedValue().Cast<IValueType::TypedBox<RpcException>>())
|
|
{
|
|
throw Exception(boxed->value.message);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReadEventException(RpcEventExceptionMap exceptions)
|
|
{
|
|
if (!exceptions || exceptions->GetCount() == 0) return;
|
|
|
|
WString message;
|
|
auto keys = exceptions->GetKeys();
|
|
for (vint i = 0; i < keys->GetCount(); i++)
|
|
{
|
|
auto key = keys->Get(i);
|
|
auto exception = UnboxValue<RpcException>(exceptions->Get(key));
|
|
message += itow(UnboxValue<vint>(key)) + L":" + exception.message + L";";
|
|
}
|
|
throw Exception(message);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
.\RPC\WFLIBRARYRPCCONTROLLER.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace rpc_controller
|
|
{
|
|
using namespace collections;
|
|
|
|
/***********************************************************************
|
|
* RpcControllerDefault
|
|
***********************************************************************/
|
|
|
|
RpcControllerDefault::RpcControllerDefault()
|
|
{
|
|
}
|
|
|
|
RpcControllerDefault::~RpcControllerDefault()
|
|
{
|
|
}
|
|
|
|
void RpcControllerDefault::Register(Ptr<IRpcObjectOps> _objectCallback, Ptr<IRpcObjectEventOps> _eventCallback)
|
|
{
|
|
objectCallback = _objectCallback;
|
|
eventCallback = _eventCallback;
|
|
}
|
|
|
|
IRpcObjectOps* RpcControllerDefault::GetObjectOps()
|
|
{
|
|
return objectCallback.Obj();
|
|
}
|
|
|
|
IRpcObjectEventOps* RpcControllerDefault::GetObjectEventOps()
|
|
{
|
|
return eventCallback.Obj();
|
|
}
|
|
|
|
void RpcControllerDefault::Finalize()
|
|
{
|
|
}
|
|
|
|
template<typename TKey>
|
|
void RpcControllerDefault::SetSuppressedFlag(Dictionary<TKey, vint>& flags, const TKey& key, bool suppressed)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcControllerDefault::SetSuppressedFlag(collections::Dictionary<TKey, vint>&, const TKey&, bool)#"
|
|
auto index = flags.Keys().IndexOf(key);
|
|
if (suppressed)
|
|
{
|
|
if (index == -1)
|
|
{
|
|
flags.Add(key, 1);
|
|
}
|
|
else
|
|
{
|
|
flags.Set(key, flags.Values()[index] + 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CHECK_ERROR(index != -1, ERROR_MESSAGE_PREFIX L"Suppression counter is not set.");
|
|
auto count = flags.Values()[index] - 1;
|
|
CHECK_ERROR(count >= 0, ERROR_MESSAGE_PREFIX L"Suppression counter is negative.");
|
|
if (count == 0)
|
|
{
|
|
flags.Remove(key);
|
|
}
|
|
else
|
|
{
|
|
flags.Set(key, count);
|
|
}
|
|
}
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
template<typename TKey>
|
|
bool RpcControllerDefault::GetSuppressedFlag(const Dictionary<TKey, vint>& flags, const TKey& key)
|
|
{
|
|
auto index = flags.Keys().IndexOf(key);
|
|
return index != -1 && flags.Values()[index] > 0;
|
|
}
|
|
|
|
void RpcControllerDefault::SetEventSuppressedFlag(RpcObjectReference ref, vint eventId, bool suppressed)
|
|
{
|
|
SetSuppressedFlag(eventSuppressedFlags, { ref,eventId }, suppressed);
|
|
}
|
|
|
|
bool RpcControllerDefault::GetEventSuppressedFlag(RpcObjectReference ref, vint eventId)
|
|
{
|
|
return GetSuppressedFlag(eventSuppressedFlags, { ref,eventId });
|
|
}
|
|
|
|
void RpcControllerDefault::SetItemChangedSuppressedFlag(RpcObjectReference ref, bool suppressed)
|
|
{
|
|
SetSuppressedFlag(itemChangedSuppressedFlags, ref, suppressed);
|
|
}
|
|
|
|
bool RpcControllerDefault::GetItemChangedSuppressedFlag(RpcObjectReference ref)
|
|
{
|
|
return GetSuppressedFlag(itemChangedSuppressedFlags, ref);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
.\RPC\WFLIBRARYRPCLIFECYCLE.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace rpc_controller
|
|
{
|
|
using namespace reflection;
|
|
using namespace reflection::description;
|
|
using namespace collections;
|
|
|
|
namespace
|
|
{
|
|
bool IsRpcWrapperObject(DescriptableObject* obj)
|
|
{
|
|
if (!obj) return false;
|
|
if (dynamic_cast<IRpcWrapperBase*>(obj)) return true;
|
|
#ifdef VCZH_DESCRIPTABLEOBJECT_WITH_METADATA
|
|
if (auto typeDescriptor = obj->GetTypeDescriptor())
|
|
{
|
|
if (auto wrapperType = GetTypeDescriptor(WString::Unmanaged(L"system::IRpcWrapperBase")))
|
|
{
|
|
return typeDescriptor->CanConvertTo(wrapperType);
|
|
}
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
|
|
WString RpcLifecycleBase::InternalProperty_LocalObjectTracker = WString::Unmanaged(L"RpcLocalObjectTracker");
|
|
WString RpcLifecycleBase::InternalProperty_WrapperTracker = WString::Unmanaged(L"RpcWrapperTracker");
|
|
|
|
RpcLocalObjectTracker::RpcLocalObjectTracker(RpcLifecycleBase* lc, RpcObjectReference r)
|
|
: lifecycle(lc), ref(r)
|
|
{
|
|
}
|
|
|
|
RpcLocalObjectTracker::~RpcLocalObjectTracker()
|
|
{
|
|
if (lifecycle && lifecycle->IsTracked(ref.objectId))
|
|
{
|
|
lifecycle->RemoveLocalObject(ref, false);
|
|
}
|
|
}
|
|
|
|
void RpcLocalObjectTracker::Attach(RpcLifecycleBase* lc, RpcObjectReference r)
|
|
{
|
|
lifecycle = lc;
|
|
ref = r;
|
|
}
|
|
|
|
void RpcLocalObjectTracker::Detach()
|
|
{
|
|
lifecycle = nullptr;
|
|
}
|
|
|
|
RpcWrapperTracker::RpcWrapperTracker(RpcLifecycleBase* lc, RpcObjectReference r)
|
|
: lifecycle(lc), ref(r)
|
|
{
|
|
}
|
|
|
|
RpcWrapperTracker::~RpcWrapperTracker()
|
|
{
|
|
if (lifecycle)
|
|
{
|
|
lifecycle->UntrackWrapper(ref);
|
|
}
|
|
}
|
|
|
|
void RpcWrapperTracker::Detach()
|
|
{
|
|
lifecycle = nullptr;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcLifecycleBase (Constructor and Setup)
|
|
***********************************************************************/
|
|
|
|
RpcLifecycleBase::RpcLifecycleBase(vint _clientId)
|
|
: clientId(_clientId)
|
|
{
|
|
}
|
|
|
|
RpcLifecycleBase::~RpcLifecycleBase()
|
|
{
|
|
}
|
|
|
|
void RpcLifecycleBase::SetIdMap(const Dictionary<WString, vint>& _idMap)
|
|
{
|
|
CopyFrom(idMap, _idMap);
|
|
}
|
|
|
|
void RpcLifecycleBase::SetSerializer(Ptr<IRpcSerializer> _serializer)
|
|
{
|
|
serializer = _serializer;
|
|
}
|
|
|
|
IRpcSerializer* RpcLifecycleBase::GetSerializer()
|
|
{
|
|
return serializer.Obj();
|
|
}
|
|
|
|
void RpcLifecycleBase::RegisterWrapperFactory(UniversalWrapperFactory factory)
|
|
{
|
|
universalWrapperFactory = factory;
|
|
}
|
|
|
|
void RpcLifecycleBase::DisconnectWrappersForFinalize()
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcLifecycleBase::DisconnectWrappersForFinalize()#"
|
|
for (auto [ref, properties] : wrapperProperties)
|
|
{
|
|
if (properties.root)
|
|
{
|
|
auto trackerObj = properties.root->GetInternalProperty(InternalProperty_WrapperTracker);
|
|
CHECK_ERROR(trackerObj, ERROR_MESSAGE_PREFIX L"Wrapper tracker missing.");
|
|
auto tracker = trackerObj.Cast<RpcWrapperTracker>();
|
|
CHECK_ERROR(tracker, ERROR_MESSAGE_PREFIX L"Invalid wrapper tracker type.");
|
|
tracker->Detach();
|
|
properties.root->SetInternalProperty(InternalProperty_WrapperTracker, nullptr);
|
|
}
|
|
|
|
if (properties.proxy)
|
|
{
|
|
properties.proxy->DisconnectFromLifecycle();
|
|
}
|
|
}
|
|
wrapperProperties.Clear();
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
vint RpcLifecycleBase::DecideTypeId(IDescriptable* obj)const
|
|
{
|
|
if (dynamic_cast<IValueObservableList*>(obj)) return RpcTypeId_IValueObservableList;
|
|
if (dynamic_cast<IValueDictionary*>(obj)) return RpcTypeId_IValueDictionary;
|
|
if (dynamic_cast<IValueArray*>(obj)) return RpcTypeId_IValueArray;
|
|
if (dynamic_cast<IValueList*>(obj)) return RpcTypeId_IValueList;
|
|
if (dynamic_cast<IValueReadonlyList*>(obj)) return RpcTypeId_IValueReadonlyList;
|
|
if (dynamic_cast<IValueEnumerator*>(obj)) return RpcTypeId_IValueEnumerator;
|
|
if (dynamic_cast<IValueEnumerable*>(obj)) return RpcTypeId_IValueEnumerable;
|
|
|
|
return RpcTypeId_NotFound;
|
|
}
|
|
|
|
void RpcLifecycleBase::TrackLocalObject(RpcObjectReference ref, IDescriptable* obj)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcLifecycleBase::TrackLocalObject(RpcObjectReference, reflection::IDescriptable*)#"
|
|
auto index = localObjectProperties.Keys().IndexOf(ref.objectId);
|
|
CHECK_ERROR(index != -1, ERROR_MESSAGE_PREFIX L"Object not registered.");
|
|
auto props = localObjectProperties.Values().Get(index);
|
|
CHECK_ERROR(props->rawPtr == nullptr, ERROR_MESSAGE_PREFIX L"Object already tracked.");
|
|
|
|
props->rawPtr = obj;
|
|
|
|
if (auto observable = dynamic_cast<IValueObservableList*>(obj))
|
|
{
|
|
auto handler = observable->ItemChanged.Add([this, ref](vint index, vint oldCount, vint newCount)
|
|
{
|
|
if (this->GetController()->GetItemChangedSuppressedFlag(ref))
|
|
{
|
|
return;
|
|
}
|
|
auto listEventOps = Ptr(new RpcCallerListEventOps(
|
|
this->GetDispatcher()->BroadcastFromClient_ObjectEventOps(this->GetClientId()),
|
|
serializer.Obj()
|
|
));
|
|
listEventOps->OnItemChanged(ref, index, oldCount, newCount);
|
|
});
|
|
props->eventHandler = handler;
|
|
}
|
|
|
|
AttachLocalObjectEvents(ref, obj);
|
|
|
|
if (auto descriptable = dynamic_cast<DescriptableObject*>(obj))
|
|
{
|
|
Ptr<RpcLocalObjectTracker> tracker;
|
|
if (auto trackerObj = descriptable->GetInternalProperty(InternalProperty_LocalObjectTracker))
|
|
{
|
|
tracker = trackerObj.Cast<RpcLocalObjectTracker>();
|
|
CHECK_ERROR(tracker, ERROR_MESSAGE_PREFIX L"Invalid local object tracker type.");
|
|
if (tracker->GetClientId() != ref.clientId)
|
|
{
|
|
throw Exception(ERROR_MESSAGE_PREFIX L"Object already tracked by a different client.");
|
|
}
|
|
CHECK_ERROR(!tracker->IsTracked(), ERROR_MESSAGE_PREFIX L"Object already tracked.");
|
|
}
|
|
else
|
|
{
|
|
tracker = Ptr(new RpcLocalObjectTracker(this, ref));
|
|
descriptable->SetInternalProperty(InternalProperty_LocalObjectTracker, tracker);
|
|
}
|
|
|
|
tracker->Attach(this, ref);
|
|
}
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
RpcObjectReference RpcLifecycleBase::CreateLocalObject(Ptr<IDescriptable> obj, RpcObjectReference ref)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcLifecycleBase::CreateLocalObject(Ptr<reflection::IDescriptable>, RpcObjectReference)#"
|
|
CHECK_ERROR(ref.clientId == clientId, ERROR_MESSAGE_PREFIX L"Ref is not local.");
|
|
CHECK_ERROR(!localObjectProperties.Keys().Contains(ref.objectId), ERROR_MESSAGE_PREFIX L"Object ID already registered.");
|
|
auto props = Ptr(new RpcLocalObjectProperties);
|
|
props->ref = ref;
|
|
props->ownedPtr = obj;
|
|
localObjectProperties.Set(ref.objectId, props);
|
|
TrackLocalObject(ref, obj.Obj());
|
|
if (nextObjectId < ref.objectId)
|
|
{
|
|
nextObjectId = ref.objectId;
|
|
}
|
|
return ref;
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RpcLifecycleBase::UntrackLocalObject(RpcObjectReference ref, bool clearInternalProperty)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcLifecycleBase::UntrackLocalObject(RpcObjectReference, bool)#"
|
|
auto index = localObjectProperties.Keys().IndexOf(ref.objectId);
|
|
CHECK_ERROR(index != -1, ERROR_MESSAGE_PREFIX L"Object not registered.");
|
|
auto props = localObjectProperties.Values().Get(index);
|
|
|
|
if (props->eventHandler)
|
|
{
|
|
if (auto observable = dynamic_cast<IValueObservableList*>(props->rawPtr))
|
|
{
|
|
observable->ItemChanged.Remove(props->eventHandler);
|
|
}
|
|
props->eventHandler = nullptr;
|
|
}
|
|
|
|
auto rawPtr = props->rawPtr;
|
|
props->rawPtr = nullptr;
|
|
|
|
if (clearInternalProperty && rawPtr)
|
|
{
|
|
if (auto descriptable = dynamic_cast<DescriptableObject*>(rawPtr))
|
|
{
|
|
if (auto trackerObj = descriptable->GetInternalProperty(InternalProperty_LocalObjectTracker))
|
|
{
|
|
auto tracker = trackerObj.Cast<RpcLocalObjectTracker>();
|
|
CHECK_ERROR(tracker, ERROR_MESSAGE_PREFIX L"Invalid local object tracker type.");
|
|
tracker->Detach();
|
|
descriptable->SetInternalProperty(InternalProperty_LocalObjectTracker, nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
props->ownedPtr = nullptr;
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RpcLifecycleBase::RemoveLocalObject(RpcObjectReference ref, bool clearInternalProperty)
|
|
{
|
|
if (localObjectProperties.Keys().Contains(ref.objectId))
|
|
{
|
|
UntrackLocalObject(ref, clearInternalProperty);
|
|
localObjectProperties.Remove(ref.objectId);
|
|
}
|
|
}
|
|
|
|
bool RpcLifecycleBase::IsTracked(vint objectId)const
|
|
{
|
|
auto index = localObjectProperties.Keys().IndexOf(objectId);
|
|
return index != -1 && localObjectProperties.Values().Get(index)->rawPtr != nullptr;
|
|
}
|
|
|
|
void RpcLifecycleBase::TrackWrapper(DescriptableObject* root, IRpcWrapperBase* proxy, RpcObjectReference ref)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcLifecycleBase::TrackWrapper(reflection::DescriptableObject*, IRpcWrapperBase*, RpcObjectReference)#"
|
|
CHECK_ERROR(root != nullptr, ERROR_MESSAGE_PREFIX L"Wrapper does not implement DescriptableObject.");
|
|
CHECK_ERROR(proxy != nullptr, ERROR_MESSAGE_PREFIX L"Wrapper is null.");
|
|
#ifdef VCZH_DESCRIPTABLEOBJECT_WITH_METADATA
|
|
root = root->SafeGetAggregationRoot();
|
|
#endif
|
|
CHECK_ERROR(root->GetInternalProperty(InternalProperty_WrapperTracker) == nullptr, ERROR_MESSAGE_PREFIX L"Wrapper already tracked.");
|
|
|
|
auto tracker = Ptr(new RpcWrapperTracker(this, ref));
|
|
root->SetInternalProperty(InternalProperty_WrapperTracker, tracker);
|
|
|
|
RpcWrapperProperties properties;
|
|
properties.root = root;
|
|
properties.proxy = proxy;
|
|
CHECK_ERROR(!wrapperProperties.Keys().Contains(ref), ERROR_MESSAGE_PREFIX L"Reference already tracked.");
|
|
wrapperProperties.Set(ref, properties);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RpcLifecycleBase::UntrackWrapper(RpcObjectReference ref)
|
|
{
|
|
wrapperProperties.Remove(ref);
|
|
}
|
|
|
|
bool RpcLifecycleBase::TryGetTrackedWrapperRef(DescriptableObject* obj, RpcObjectReference& ref)const
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcLifecycleBase::TryGetTrackedWrapperRef(reflection::DescriptableObject*, RpcObjectReference&)const#"
|
|
auto wrapperRoot = obj;
|
|
#ifdef VCZH_DESCRIPTABLEOBJECT_WITH_METADATA
|
|
if (wrapperRoot)
|
|
{
|
|
wrapperRoot = wrapperRoot->SafeGetAggregationRoot();
|
|
}
|
|
#endif
|
|
|
|
if (wrapperRoot)
|
|
{
|
|
if (auto trackerObj = wrapperRoot->GetInternalProperty(InternalProperty_WrapperTracker))
|
|
{
|
|
auto tracker = trackerObj.Cast<RpcWrapperTracker>();
|
|
CHECK_ERROR(tracker, ERROR_MESSAGE_PREFIX L"Invalid internal property type.");
|
|
CHECK_ERROR(tracker->GetLifecycle() == this, ERROR_MESSAGE_PREFIX L"Wrapper registered to a different lifecycle.");
|
|
ref = tracker->GetRef();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
IRpcWrapperBase* RpcLifecycleBase::GetTrackedWrapper(RpcObjectReference ref)const
|
|
{
|
|
if (auto index = wrapperProperties.Keys().IndexOf(ref); index != -1)
|
|
{
|
|
return wrapperProperties.Values()[index].proxy;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Ptr<IDescriptable> RpcLifecycleBase::CreateCallerProxy(RpcObjectReference ref, IRpcSerializer* serializer)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcLifecycleBase::CreateCallerProxy(RpcObjectReference)#"
|
|
IRpcLifecycle* lc = this;
|
|
Ptr<IRpcWrapperBase> wrapper;
|
|
|
|
switch (ref.typeId)
|
|
{
|
|
case RpcTypeId_IValueEnumerable:
|
|
wrapper = Ptr(new RpcByrefEnumerable(lc, ref, serializer));
|
|
break;
|
|
case RpcTypeId_IValueEnumerator:
|
|
wrapper = Ptr(new RpcByrefEnumerator(lc, ref, serializer));
|
|
break;
|
|
case RpcTypeId_IValueArray:
|
|
wrapper = Ptr(new RpcByrefArray(lc, ref, serializer));
|
|
break;
|
|
case RpcTypeId_IValueReadonlyList:
|
|
wrapper = Ptr(new RpcByrefReadonlyList(lc, ref, serializer));
|
|
break;
|
|
case RpcTypeId_IValueList:
|
|
wrapper = Ptr(new RpcByrefList(lc, ref, serializer));
|
|
break;
|
|
case RpcTypeId_IValueObservableList:
|
|
wrapper = Ptr(new RpcByrefObservableList(lc, ref, serializer));
|
|
break;
|
|
case RpcTypeId_IValueDictionary:
|
|
wrapper = Ptr(new RpcByrefDictionary(lc, ref, serializer));
|
|
break;
|
|
default:
|
|
CHECK_ERROR(universalWrapperFactory, ERROR_MESSAGE_PREFIX L"No wrapper factory registered.");
|
|
wrapper = universalWrapperFactory(ref, this);
|
|
CHECK_ERROR(wrapper, ERROR_MESSAGE_PREFIX L"Wrapper factory returned null.");
|
|
break;
|
|
}
|
|
|
|
auto descriptable = dynamic_cast<IDescriptable*>(wrapper.Obj());
|
|
CHECK_ERROR(descriptable, ERROR_MESSAGE_PREFIX L"Wrapper does not implement IDescriptable.");
|
|
auto root = dynamic_cast<DescriptableObject*>(descriptable);
|
|
TrackWrapper(root, wrapper.Obj(), ref);
|
|
return Ptr(descriptable);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcLifecycleBase (IRpcLifecycle)
|
|
***********************************************************************/
|
|
|
|
void RpcLifecycleBase::Finalize()
|
|
{
|
|
DisconnectWrappersForFinalize();
|
|
while (localObjectProperties.Count() > 0)
|
|
{
|
|
auto props = localObjectProperties.Values().Get(localObjectProperties.Count() - 1);
|
|
RemoveLocalObject(props->ref, true);
|
|
}
|
|
registeredLocalServices.Clear();
|
|
registeredRemoteServices.Clear();
|
|
controller.Finalize();
|
|
}
|
|
|
|
void RpcLifecycleBase::Initialize()
|
|
{
|
|
if (!initialized)
|
|
{
|
|
GetDispatcher()->Initialize();
|
|
initialized = true;
|
|
}
|
|
}
|
|
|
|
vint RpcLifecycleBase::GetClientId()
|
|
{
|
|
return clientId;
|
|
}
|
|
|
|
RpcControllerDefault* RpcLifecycleBase::GetController()
|
|
{
|
|
return &controller;
|
|
}
|
|
|
|
const RpcLocalServiceMap& RpcLifecycleBase::GetRegisteredLocalServices()
|
|
{
|
|
return registeredLocalServices;
|
|
}
|
|
|
|
void RpcLifecycleBase::LocalObjectHold(RpcObjectReference ref, vint remoteClientId)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcLifecycleBase::LocalObjectHold(RpcObjectReference, vint)#"
|
|
CHECK_ERROR(ref.clientId == clientId, ERROR_MESSAGE_PREFIX L"Ref is not local.");
|
|
auto index = localObjectProperties.Keys().IndexOf(ref.objectId);
|
|
CHECK_ERROR(index != -1, ERROR_MESSAGE_PREFIX L"Object not registered.");
|
|
auto props = localObjectProperties.Values().Get(index);
|
|
if (!props->interestedClients.Contains(remoteClientId))
|
|
{
|
|
props->interestedClients.Add(remoteClientId);
|
|
}
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RpcLifecycleBase::LocalObjectUnhold(RpcObjectReference ref, vint remoteClientId)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcLifecycleBase::LocalObjectUnhold(RpcObjectReference, vint)#"
|
|
CHECK_ERROR(ref.clientId == clientId, ERROR_MESSAGE_PREFIX L"Ref is not local.");
|
|
auto index = localObjectProperties.Keys().IndexOf(ref.objectId);
|
|
CHECK_ERROR(index != -1, ERROR_MESSAGE_PREFIX L"Object not registered.");
|
|
auto props = localObjectProperties.Values().Get(index);
|
|
CHECK_ERROR(props->interestedClients.Contains(remoteClientId), ERROR_MESSAGE_PREFIX L"Client does not hold this object.");
|
|
props->interestedClients.Remove(remoteClientId);
|
|
if (props->interestedClients.Count() == 0)
|
|
{
|
|
RemoveLocalObject(ref, true);
|
|
}
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RpcLifecycleBase::RegisterLocalService(vint typeId, Ptr<IDescriptable> service)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcLifecycleBase::RegisterLocalService(vint, Ptr<IDescriptable>)#"
|
|
CHECK_ERROR(service, ERROR_MESSAGE_PREFIX L"Service is required.");
|
|
if (initialized)
|
|
{
|
|
throw Exception(ERROR_MESSAGE_PREFIX L"RegisterLocalService cannot be called after Initialize.");
|
|
}
|
|
if (registeredLocalServices.Keys().Contains(typeId))
|
|
{
|
|
throw Exception(ERROR_MESSAGE_PREFIX L"Service is already registered.");
|
|
}
|
|
|
|
auto ref = CreateLocalObject(service, RpcObjectReference{ clientId, typeId, typeId });
|
|
LocalObjectHold(ref, clientId);
|
|
registeredLocalServices.Set(typeId, service);
|
|
GetDispatcher()->DeclareLocalService(ref);
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RpcLifecycleBase::DeclareRemoteService(RpcObjectReference ref)
|
|
{
|
|
registeredRemoteServices.Set(ref.typeId, ref);
|
|
}
|
|
|
|
vint RpcLifecycleBase::GetTypeIdFromName(WString typeName)
|
|
{
|
|
auto typeIndex = idMap.Keys().IndexOf(typeName);
|
|
if (typeIndex == -1)
|
|
{
|
|
return RpcTypeId_NotFound;
|
|
}
|
|
return idMap.Values()[typeIndex];
|
|
}
|
|
|
|
Ptr<IDescriptable> RpcLifecycleBase::RequestService(WString typeName)
|
|
{
|
|
auto typeId = GetTypeIdFromName(typeName);
|
|
if (typeId == RpcTypeId_NotFound)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (auto index = registeredLocalServices.Keys().IndexOf(typeId); index != -1)
|
|
{
|
|
return registeredLocalServices.Values()[index];
|
|
}
|
|
|
|
if (auto index = registeredRemoteServices.Keys().IndexOf(typeId); index != -1)
|
|
{
|
|
return RefToPtr(registeredRemoteServices.Values()[index]);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Ptr<IDescriptable> RpcLifecycleBase::RefToPtr(RpcObjectReference ref)
|
|
{
|
|
return RefToPtr(ref, serializer.Obj());
|
|
}
|
|
|
|
Ptr<IDescriptable> RpcLifecycleBase::RefToPtr(RpcObjectReference ref, IRpcSerializer* serializer)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcLifecycleBase::RefToPtr(RpcObjectReference)#"
|
|
if (ref.clientId == clientId)
|
|
{
|
|
auto index = localObjectProperties.Keys().IndexOf(ref.objectId);
|
|
CHECK_ERROR(index != -1, ERROR_MESSAGE_PREFIX L"Object not registered.");
|
|
auto props = localObjectProperties.Values().Get(index);
|
|
CHECK_ERROR(props->rawPtr != nullptr, ERROR_MESSAGE_PREFIX L"Object not tracked.");
|
|
return Ptr(props->rawPtr);
|
|
}
|
|
else
|
|
{
|
|
if (auto index = wrapperProperties.Keys().IndexOf(ref); index != -1)
|
|
{
|
|
auto proxy = wrapperProperties.Values()[index].proxy;
|
|
auto descriptable = dynamic_cast<IDescriptable*>(proxy);
|
|
CHECK_ERROR(descriptable, ERROR_MESSAGE_PREFIX L"Wrapper does not implement IDescriptable.");
|
|
return Ptr(descriptable);
|
|
}
|
|
|
|
return CreateCallerProxy(ref, serializer);
|
|
}
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
RpcObjectReference RpcLifecycleBase::PtrToRef(Ptr<IDescriptable> obj)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcLifecycleBase::PtrToRef(Ptr<reflection::IDescriptable>)#"
|
|
CHECK_ERROR(obj, ERROR_MESSAGE_PREFIX L"A value is required.");
|
|
|
|
if (auto descObj = dynamic_cast<DescriptableObject*>(obj.Obj()))
|
|
{
|
|
RpcObjectReference wrapperRef;
|
|
if (TryGetTrackedWrapperRef(descObj, wrapperRef))
|
|
{
|
|
return wrapperRef;
|
|
}
|
|
|
|
if (IsRpcWrapperObject(descObj))
|
|
{
|
|
CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Wrapper tracker lookup unexpectedly failed.");
|
|
}
|
|
}
|
|
|
|
if (auto descObj = dynamic_cast<DescriptableObject*>(obj.Obj()))
|
|
{
|
|
if (auto trackerObj = descObj->GetInternalProperty(InternalProperty_LocalObjectTracker))
|
|
{
|
|
auto tracker = trackerObj.Cast<RpcLocalObjectTracker>();
|
|
CHECK_ERROR(tracker, ERROR_MESSAGE_PREFIX L"Invalid internal property type.");
|
|
if (tracker->GetClientId() != clientId)
|
|
{
|
|
throw Exception(ERROR_MESSAGE_PREFIX L"Object registered to a different lifecycle.");
|
|
}
|
|
if (tracker->IsTracked())
|
|
{
|
|
CHECK_ERROR(tracker->GetLifecycle() == this, ERROR_MESSAGE_PREFIX L"Object tracker registered to an invalid lifecycle instance.");
|
|
return tracker->GetRef();
|
|
}
|
|
}
|
|
}
|
|
|
|
auto typeId = DecideTypeId(obj.Obj());
|
|
CHECK_ERROR(typeId != RpcTypeId_NotFound, ERROR_MESSAGE_PREFIX L"DecideTypeId returned RpcTypeId_NotFound (unknown type).");
|
|
return CreateLocalObject(obj, RpcObjectReference{ clientId, ++nextObjectId, typeId });
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
.\RPC\WFLIBRARYRPCWRAPPERS.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
using namespace collections;
|
|
|
|
namespace rpc_controller
|
|
{
|
|
using namespace reflection;
|
|
using namespace reflection::description;
|
|
|
|
namespace
|
|
{
|
|
#ifndef VCZH_WORKFLOW_RPC_OBJECT_REFERENCE_VALUE_HELPERS
|
|
#define VCZH_WORKFLOW_RPC_OBJECT_REFERENCE_VALUE_HELPERS
|
|
bool IsRpcObjectReferenceValue(const Value& value)
|
|
{
|
|
return value.GetValueType() == Value::BoxedValue && value.GetBoxedValue().Cast<IValueType::TypedBox<RpcObjectReference>>();
|
|
}
|
|
|
|
RpcObjectReference GetRpcObjectReference(const Value& value)
|
|
{
|
|
auto boxed = value.GetBoxedValue().Cast<IValueType::TypedBox<RpcObjectReference>>();
|
|
CHECK_ERROR(boxed, L"RpcObjectReference is expected.");
|
|
return boxed->value;
|
|
}
|
|
#endif
|
|
|
|
IRpcWrapperBase* CastRpcWrapperBase(IDescriptable* obj)
|
|
{
|
|
if (!obj) return nullptr;
|
|
if (auto wrapper = dynamic_cast<IRpcWrapperBase*>(obj)) return wrapper;
|
|
return obj->SafeAggregationCast<IRpcWrapperBase>();
|
|
}
|
|
|
|
template<typename TInterface>
|
|
TInterface* CastRpcInterface(IDescriptable* obj)
|
|
{
|
|
if (!obj) return nullptr;
|
|
if (auto proxy = dynamic_cast<TInterface*>(obj)) return proxy;
|
|
return obj->SafeAggregationCast<TInterface>();
|
|
}
|
|
|
|
template<typename TWrapper, typename TInterface>
|
|
Ptr<TInterface> CreateTrackedProxy(IRpcLifecycle* lc, RpcObjectReference ref, IRpcSerializer* serializer)
|
|
{
|
|
auto obj = [&]()
|
|
{
|
|
if (auto lifecycle = dynamic_cast<RpcLifecycleBase*>(lc))
|
|
{
|
|
return lifecycle->RefToPtr(ref, serializer);
|
|
}
|
|
return lc->RefToPtr(ref);
|
|
}();
|
|
CHECK_ERROR(obj, L"RPC proxy creation returned null.");
|
|
if (auto wrapper = Ptr(CastRpcWrapperBase(obj.Obj())))
|
|
{
|
|
auto proxy = Ptr(CastRpcInterface<TInterface>(obj.Obj()));
|
|
CHECK_ERROR(proxy, L"RPC proxy creation returned an unexpected type.");
|
|
return proxy;
|
|
}
|
|
return Ptr(new TWrapper(lc, ref, serializer));
|
|
}
|
|
|
|
void SetRemoteObjectHold(IRpcLifecycle* lc, RpcObjectReference ref, bool hold)
|
|
{
|
|
lc->GetDispatcher()->SendToClient_ObjectOps(ref.clientId)->ObjectHold(ref, lc->GetClientId(), hold);
|
|
}
|
|
|
|
Ptr<IRpcListOps> GetRemoteListOps(IRpcLifecycle* lc, RpcObjectReference ref, IRpcSerializer* serializer)
|
|
{
|
|
return Ptr(new RpcCallerListOps(lc->GetDispatcher()->SendToClient_ObjectOps(ref.clientId), serializer));
|
|
}
|
|
|
|
Value SerializeValue(IRpcSerializer* serializer, const Value& value)
|
|
{
|
|
return serializer ? serializer->Serialize(value) : value;
|
|
}
|
|
|
|
Value DeserializeValue(IRpcSerializer* serializer, const Value& value)
|
|
{
|
|
return serializer ? serializer->Deserialize(value) : value;
|
|
}
|
|
|
|
template<typename ...TArgs>
|
|
Ptr<IValueArray> CreateRpcArguments(TArgs&& ...args)
|
|
{
|
|
auto arguments = IValueArray::Create();
|
|
arguments->Resize(sizeof...(TArgs));
|
|
vint index = 0;
|
|
((arguments->Set(index++, std::forward<TArgs>(args))), ...);
|
|
return arguments;
|
|
}
|
|
|
|
Value ReadMethodResult(IRpcSerializer* serializer, const Value& value)
|
|
{
|
|
auto deserialized = DeserializeValue(serializer, value);
|
|
ReadMethodException(deserialized);
|
|
return deserialized;
|
|
}
|
|
|
|
Value InvokeListMethod(IRpcObjectOps* objectOps, IRpcSerializer* serializer, RpcObjectReference ref, vint methodId, Ptr<IValueArray> arguments)
|
|
{
|
|
auto result = objectOps->InvokeMethod(ref, methodId, arguments);
|
|
return ReadMethodResult(serializer, result);
|
|
}
|
|
|
|
bool IsRpcListMethodId(vint methodId)
|
|
{
|
|
switch (methodId)
|
|
{
|
|
case RpcMethodId_IValueEnumerable_CreateEnumerator:
|
|
case RpcMethodId_IValueEnumerator_Next:
|
|
case RpcMethodId_IValueEnumerator_GetCurrent:
|
|
case RpcMethodId_IValueReadonlyList_GetCount:
|
|
case RpcMethodId_IValueReadonlyList_Get:
|
|
case RpcMethodId_IValueList_Set:
|
|
case RpcMethodId_IValueList_Add:
|
|
case RpcMethodId_IValueList_Insert:
|
|
case RpcMethodId_IValueList_RemoveAt:
|
|
case RpcMethodId_IValueList_Clear:
|
|
case RpcMethodId_IValueReadonlyList_Contains:
|
|
case RpcMethodId_IValueReadonlyList_IndexOf:
|
|
case RpcMethodId_IValueReadonlyDictionary_GetCount:
|
|
case RpcMethodId_IValueReadonlyDictionary_Get:
|
|
case RpcMethodId_IValueDictionary_Set:
|
|
case RpcMethodId_IValueDictionary_Remove:
|
|
case RpcMethodId_IValueDictionary_Clear:
|
|
case RpcMethodId_IValueReadonlyDictionary_ContainsKey:
|
|
case RpcMethodId_IValueReadonlyDictionary_GetKeys:
|
|
case RpcMethodId_IValueReadonlyDictionary_GetValues:
|
|
case RpcMethodId_IValueArray_Resize:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Value RpcBoxValueByref(const Value& trivial, IRpcLifecycle* lc)
|
|
{
|
|
if (!lc) CHECK_FAIL(L"IRpcLifecycle cannot be null.");
|
|
if (trivial.IsNull()) return trivial;
|
|
|
|
if (trivial.GetValueType() == Value::SharedPtr)
|
|
{
|
|
if (auto raw = trivial.GetRawPtr())
|
|
{
|
|
if (auto obj = dynamic_cast<IDescriptable*>(raw))
|
|
{
|
|
return BoxValue(RpcBoxByref(Ptr<IDescriptable>(obj), lc));
|
|
}
|
|
}
|
|
}
|
|
|
|
return trivial;
|
|
}
|
|
|
|
Value RpcUnboxValueByref(const Value& serializable, IRpcLifecycle* lc)
|
|
{
|
|
if (!lc) CHECK_FAIL(L"IRpcLifecycle cannot be null.");
|
|
if (serializable.IsNull()) return serializable;
|
|
|
|
if (IsRpcObjectReferenceValue(serializable))
|
|
{
|
|
auto ref = GetRpcObjectReference(serializable);
|
|
auto obj = RpcUnboxByref(ref, lc);
|
|
return BoxValue(obj);
|
|
}
|
|
|
|
return serializable;
|
|
}
|
|
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcByrefEnumerator
|
|
***********************************************************************/
|
|
|
|
RpcByrefEnumerator::RpcByrefEnumerator(IRpcLifecycle* lc, RpcObjectReference enumeratorRef, IRpcSerializer* _serializer)
|
|
: lifecycle(lc)
|
|
, serializer(_serializer)
|
|
, ref(enumeratorRef)
|
|
{
|
|
if (!lifecycle) CHECK_FAIL(L"Invalid IRpcLifecycle.");
|
|
SetRemoteObjectHold(lifecycle, ref, true);
|
|
}
|
|
|
|
RpcByrefEnumerator::~RpcByrefEnumerator()
|
|
{
|
|
if (lifecycle) SetRemoteObjectHold(lifecycle, ref, false);
|
|
}
|
|
|
|
void RpcByrefEnumerator::DisconnectFromLifecycle()
|
|
{
|
|
lifecycle = nullptr;
|
|
}
|
|
|
|
Value RpcByrefEnumerator::GetCurrent()
|
|
{
|
|
return RpcUnboxValueByref(GetRemoteListOps(lifecycle, ref, serializer)->EnumGetCurrent(ref), lifecycle);
|
|
}
|
|
|
|
vint RpcByrefEnumerator::GetIndex()
|
|
{
|
|
return index;
|
|
}
|
|
|
|
bool RpcByrefEnumerator::Next()
|
|
{
|
|
if (GetRemoteListOps(lifecycle, ref, serializer)->EnumNext(ref))
|
|
{
|
|
index++;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RpcByrefEnumerable::RpcByrefEnumerable(IRpcLifecycle* lc, RpcObjectReference enumerableRef, IRpcSerializer* _serializer)
|
|
: lifecycle(lc)
|
|
, serializer(_serializer)
|
|
, ref(enumerableRef)
|
|
{
|
|
if (!lifecycle) CHECK_FAIL(L"Invalid IRpcLifecycle.");
|
|
SetRemoteObjectHold(lifecycle, ref, true);
|
|
}
|
|
|
|
RpcByrefEnumerable::~RpcByrefEnumerable()
|
|
{
|
|
if (lifecycle) SetRemoteObjectHold(lifecycle, ref, false);
|
|
}
|
|
|
|
void RpcByrefEnumerable::DisconnectFromLifecycle()
|
|
{
|
|
lifecycle = nullptr;
|
|
}
|
|
|
|
Ptr<IValueEnumerator> RpcByrefEnumerable::CreateEnumerator()
|
|
{
|
|
return CreateTrackedProxy<RpcByrefEnumerator, IValueEnumerator>(lifecycle, GetRemoteListOps(lifecycle, ref, serializer)->EnumCreate(ref), serializer);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcByrefReadonlyList
|
|
***********************************************************************/
|
|
|
|
RpcByrefReadonlyList::RpcByrefReadonlyList(IRpcLifecycle* lc, RpcObjectReference listRef, IRpcSerializer* _serializer)
|
|
: lifecycle(lc)
|
|
, serializer(_serializer)
|
|
, ref(listRef)
|
|
{
|
|
if (!lifecycle) CHECK_FAIL(L"Invalid IRpcLifecycle.");
|
|
SetRemoteObjectHold(lifecycle, ref, true);
|
|
}
|
|
|
|
RpcByrefReadonlyList::~RpcByrefReadonlyList()
|
|
{
|
|
if (lifecycle) SetRemoteObjectHold(lifecycle, ref, false);
|
|
}
|
|
|
|
void RpcByrefReadonlyList::DisconnectFromLifecycle()
|
|
{
|
|
lifecycle = nullptr;
|
|
}
|
|
|
|
Ptr<IValueEnumerator> RpcByrefReadonlyList::CreateEnumerator()
|
|
{
|
|
return CreateTrackedProxy<RpcByrefEnumerator, IValueEnumerator>(lifecycle, GetRemoteListOps(lifecycle, ref, serializer)->EnumCreate(ref), serializer);
|
|
}
|
|
|
|
vint RpcByrefReadonlyList::GetCount()
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListGetCount(ref);
|
|
}
|
|
|
|
Value RpcByrefReadonlyList::Get(vint index)
|
|
{
|
|
return RpcUnboxValueByref(GetRemoteListOps(lifecycle, ref, serializer)->ListGet(ref, index), lifecycle);
|
|
}
|
|
|
|
bool RpcByrefReadonlyList::Contains(const Value& value)
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListContains(ref, SerializeValue(serializer, RpcBoxValueByref(value, lifecycle)));
|
|
}
|
|
|
|
vint RpcByrefReadonlyList::IndexOf(const Value& value)
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListIndexOf(ref, SerializeValue(serializer, RpcBoxValueByref(value, lifecycle)));
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcByrefList
|
|
***********************************************************************/
|
|
|
|
RpcByrefList::RpcByrefList(IRpcLifecycle* lc, RpcObjectReference listRef, IRpcSerializer* _serializer)
|
|
: RpcByrefReadonlyList(lc, listRef, _serializer)
|
|
{
|
|
}
|
|
|
|
RpcByrefList::~RpcByrefList()
|
|
{
|
|
}
|
|
|
|
Ptr<IValueEnumerator> RpcByrefList::CreateEnumerator()
|
|
{
|
|
return RpcByrefReadonlyList::CreateEnumerator();
|
|
}
|
|
|
|
vint RpcByrefList::GetCount()
|
|
{
|
|
return RpcByrefReadonlyList::GetCount();
|
|
}
|
|
|
|
Value RpcByrefList::Get(vint index)
|
|
{
|
|
return RpcByrefReadonlyList::Get(index);
|
|
}
|
|
|
|
bool RpcByrefList::Contains(const Value& value)
|
|
{
|
|
return RpcByrefReadonlyList::Contains(value);
|
|
}
|
|
|
|
vint RpcByrefList::IndexOf(const Value& value)
|
|
{
|
|
return RpcByrefReadonlyList::IndexOf(value);
|
|
}
|
|
|
|
void RpcByrefList::Set(vint index, const Value& value)
|
|
{
|
|
GetRemoteListOps(lifecycle, ref, serializer)->ListSet(ref, index, SerializeValue(serializer, RpcBoxValueByref(value, lifecycle)));
|
|
}
|
|
|
|
vint RpcByrefList::Add(const Value& value)
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListAdd(ref, SerializeValue(serializer, RpcBoxValueByref(value, lifecycle)));
|
|
}
|
|
|
|
vint RpcByrefList::Insert(vint index, const Value& value)
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListInsert(ref, index, SerializeValue(serializer, RpcBoxValueByref(value, lifecycle)));
|
|
}
|
|
|
|
bool RpcByrefList::Remove(const Value& value)
|
|
{
|
|
auto index = IndexOf(value);
|
|
return index == -1 ? false : GetRemoteListOps(lifecycle, ref, serializer)->ListRemoveAt(ref, index);
|
|
}
|
|
|
|
bool RpcByrefList::RemoveAt(vint index)
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListRemoveAt(ref, index);
|
|
}
|
|
|
|
void RpcByrefList::Clear()
|
|
{
|
|
GetRemoteListOps(lifecycle, ref, serializer)->ListClear(ref);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcByrefArray
|
|
***********************************************************************/
|
|
|
|
RpcByrefArray::RpcByrefArray(IRpcLifecycle* lc, RpcObjectReference arrayRef, IRpcSerializer* _serializer)
|
|
: lifecycle(lc)
|
|
, serializer(_serializer)
|
|
, ref(arrayRef)
|
|
{
|
|
if (!lifecycle) CHECK_FAIL(L"Invalid IRpcLifecycle.");
|
|
SetRemoteObjectHold(lifecycle, ref, true);
|
|
}
|
|
|
|
RpcByrefArray::~RpcByrefArray()
|
|
{
|
|
if (lifecycle) SetRemoteObjectHold(lifecycle, ref, false);
|
|
}
|
|
|
|
void RpcByrefArray::DisconnectFromLifecycle()
|
|
{
|
|
lifecycle = nullptr;
|
|
}
|
|
|
|
Ptr<IValueEnumerator> RpcByrefArray::CreateEnumerator()
|
|
{
|
|
return CreateTrackedProxy<RpcByrefEnumerator, IValueEnumerator>(lifecycle, GetRemoteListOps(lifecycle, ref, serializer)->EnumCreate(ref), serializer);
|
|
}
|
|
|
|
vint RpcByrefArray::GetCount()
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListGetCount(ref);
|
|
}
|
|
|
|
Value RpcByrefArray::Get(vint index)
|
|
{
|
|
return RpcUnboxValueByref(GetRemoteListOps(lifecycle, ref, serializer)->ListGet(ref, index), lifecycle);
|
|
}
|
|
|
|
bool RpcByrefArray::Contains(const Value& value)
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListContains(ref, SerializeValue(serializer, RpcBoxValueByref(value, lifecycle)));
|
|
}
|
|
|
|
vint RpcByrefArray::IndexOf(const Value& value)
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListIndexOf(ref, SerializeValue(serializer, RpcBoxValueByref(value, lifecycle)));
|
|
}
|
|
|
|
void RpcByrefArray::Set(vint index, const Value& value)
|
|
{
|
|
GetRemoteListOps(lifecycle, ref, serializer)->ListSet(ref, index, SerializeValue(serializer, RpcBoxValueByref(value, lifecycle)));
|
|
}
|
|
|
|
void RpcByrefArray::Resize(vint size)
|
|
{
|
|
GetRemoteListOps(lifecycle, ref, serializer)->ArrayResize(ref, size);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcByrefObservableList
|
|
***********************************************************************/
|
|
|
|
RpcByrefObservableList::RpcByrefObservableList(IRpcLifecycle* lc, RpcObjectReference listRef, IRpcSerializer* _serializer)
|
|
: lifecycle(lc)
|
|
, serializer(_serializer)
|
|
, ref(listRef)
|
|
{
|
|
if (!lifecycle) CHECK_FAIL(L"Invalid IRpcLifecycle.");
|
|
SetRemoteObjectHold(lifecycle, ref, true);
|
|
}
|
|
|
|
RpcByrefObservableList::~RpcByrefObservableList()
|
|
{
|
|
if (lifecycle) SetRemoteObjectHold(lifecycle, ref, false);
|
|
}
|
|
|
|
void RpcByrefObservableList::DisconnectFromLifecycle()
|
|
{
|
|
lifecycle = nullptr;
|
|
}
|
|
|
|
Ptr<IValueEnumerator> RpcByrefObservableList::CreateEnumerator()
|
|
{
|
|
return CreateTrackedProxy<RpcByrefEnumerator, IValueEnumerator>(lifecycle, GetRemoteListOps(lifecycle, ref, serializer)->EnumCreate(ref), serializer);
|
|
}
|
|
|
|
vint RpcByrefObservableList::GetCount()
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListGetCount(ref);
|
|
}
|
|
|
|
Value RpcByrefObservableList::Get(vint index)
|
|
{
|
|
return RpcUnboxValueByref(GetRemoteListOps(lifecycle, ref, serializer)->ListGet(ref, index), lifecycle);
|
|
}
|
|
|
|
bool RpcByrefObservableList::Contains(const Value& value)
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListContains(ref, SerializeValue(serializer, RpcBoxValueByref(value, lifecycle)));
|
|
}
|
|
|
|
vint RpcByrefObservableList::IndexOf(const Value& value)
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListIndexOf(ref, SerializeValue(serializer, RpcBoxValueByref(value, lifecycle)));
|
|
}
|
|
|
|
void RpcByrefObservableList::Set(vint index, const Value& value)
|
|
{
|
|
GetRemoteListOps(lifecycle, ref, serializer)->ListSet(ref, index, SerializeValue(serializer, RpcBoxValueByref(value, lifecycle)));
|
|
}
|
|
|
|
vint RpcByrefObservableList::Add(const Value& value)
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListAdd(ref, SerializeValue(serializer, RpcBoxValueByref(value, lifecycle)));
|
|
}
|
|
|
|
vint RpcByrefObservableList::Insert(vint index, const Value& value)
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListInsert(ref, index, SerializeValue(serializer, RpcBoxValueByref(value, lifecycle)));
|
|
}
|
|
|
|
bool RpcByrefObservableList::Remove(const Value& value)
|
|
{
|
|
auto index = IndexOf(value);
|
|
return index == -1 ? false : GetRemoteListOps(lifecycle, ref, serializer)->ListRemoveAt(ref, index);
|
|
}
|
|
|
|
bool RpcByrefObservableList::RemoveAt(vint index)
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->ListRemoveAt(ref, index);
|
|
}
|
|
|
|
void RpcByrefObservableList::Clear()
|
|
{
|
|
GetRemoteListOps(lifecycle, ref, serializer)->ListClear(ref);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcByrefDictionary
|
|
***********************************************************************/
|
|
|
|
RpcByrefDictionary::RpcByrefDictionary(IRpcLifecycle* lc, RpcObjectReference dictRef, IRpcSerializer* _serializer)
|
|
: lifecycle(lc)
|
|
, serializer(_serializer)
|
|
, ref(dictRef)
|
|
{
|
|
if (!lifecycle) CHECK_FAIL(L"Invalid IRpcLifecycle.");
|
|
SetRemoteObjectHold(lifecycle, ref, true);
|
|
}
|
|
|
|
RpcByrefDictionary::~RpcByrefDictionary()
|
|
{
|
|
if (lifecycle) SetRemoteObjectHold(lifecycle, ref, false);
|
|
}
|
|
|
|
void RpcByrefDictionary::DisconnectFromLifecycle()
|
|
{
|
|
lifecycle = nullptr;
|
|
}
|
|
|
|
Ptr<IValueReadonlyList> RpcByrefDictionary::GetKeys()
|
|
{
|
|
return CreateTrackedProxy<RpcByrefReadonlyList, IValueReadonlyList>(lifecycle, GetRemoteListOps(lifecycle, ref, serializer)->DictGetKeys(ref), serializer);
|
|
}
|
|
|
|
Ptr<IValueReadonlyList> RpcByrefDictionary::GetValues()
|
|
{
|
|
return CreateTrackedProxy<RpcByrefReadonlyList, IValueReadonlyList>(lifecycle, GetRemoteListOps(lifecycle, ref, serializer)->DictGetValues(ref), serializer);
|
|
}
|
|
|
|
vint RpcByrefDictionary::GetCount()
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->DictGetCount(ref);
|
|
}
|
|
|
|
Value RpcByrefDictionary::Get(const Value& key)
|
|
{
|
|
auto serializedKey = SerializeValue(serializer, RpcBoxValueByref(key, lifecycle));
|
|
auto serializedValue = GetRemoteListOps(lifecycle, ref, serializer)->DictGet(ref, serializedKey);
|
|
return RpcUnboxValueByref(serializedValue, lifecycle);
|
|
}
|
|
|
|
void RpcByrefDictionary::Set(const Value& key, const Value& value)
|
|
{
|
|
auto serializedKey = SerializeValue(serializer, RpcBoxValueByref(key, lifecycle));
|
|
auto serializedValue = SerializeValue(serializer, RpcBoxValueByref(value, lifecycle));
|
|
GetRemoteListOps(lifecycle, ref, serializer)->DictSet(ref, serializedKey, serializedValue);
|
|
}
|
|
|
|
bool RpcByrefDictionary::Remove(const Value& key)
|
|
{
|
|
return GetRemoteListOps(lifecycle, ref, serializer)->DictRemove(ref, SerializeValue(serializer, RpcBoxValueByref(key, lifecycle)));
|
|
}
|
|
|
|
void RpcByrefDictionary::Clear()
|
|
{
|
|
GetRemoteListOps(lifecycle, ref, serializer)->DictClear(ref);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcCalleeListOps
|
|
***********************************************************************/
|
|
|
|
RpcCalleeListOps::RpcCalleeListOps(IRpcLifecycle* lc, IRpcSerializer* _serializer)
|
|
: lifecycle(lc)
|
|
, serializer(_serializer)
|
|
{
|
|
if (!lifecycle) CHECK_FAIL(L"Invalid IRpcLifecycle.");
|
|
}
|
|
|
|
RpcObjectReference RpcCalleeListOps::EnumCreate(RpcObjectReference ref)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
Ptr<IValueEnumerator> enumerator;
|
|
if (auto xs = Ptr(obj.Obj()->SafeAggregationCast<IValueEnumerable>()))
|
|
{
|
|
enumerator = xs->CreateEnumerator();
|
|
}
|
|
else
|
|
{
|
|
throw Exception(L"RpcCalleeListOps::EnumCreate cannot find the target collection.");
|
|
}
|
|
|
|
return lifecycle->PtrToRef(enumerator);
|
|
}
|
|
|
|
bool RpcCalleeListOps::EnumNext(RpcObjectReference enumerator)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(enumerator);
|
|
auto e = Ptr(obj.Obj()->SafeAggregationCast<IValueEnumerator>());
|
|
if (!e) throw Exception(L"RpcCalleeListOps::EnumNext cannot find the target enumerator.");
|
|
return e->Next();
|
|
}
|
|
|
|
Value RpcCalleeListOps::EnumGetCurrent(RpcObjectReference enumerator)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(enumerator);
|
|
auto e = Ptr(obj.Obj()->SafeAggregationCast<IValueEnumerator>());
|
|
if (!e) throw Exception(L"RpcCalleeListOps::EnumGetCurrent cannot find the target enumerator.");
|
|
return SerializeValue(serializer, RpcBoxValueByref(e->GetCurrent(), lifecycle));
|
|
}
|
|
|
|
vint RpcCalleeListOps::ListGetCount(RpcObjectReference ref)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
if (auto roList = Ptr(obj.Obj()->SafeAggregationCast<IValueReadonlyList>()))
|
|
return roList->GetCount();
|
|
throw Exception(L"RpcCalleeListOps::ListGetCount cannot find the target list.");
|
|
}
|
|
|
|
Value RpcCalleeListOps::ListGet(RpcObjectReference ref, vint index)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
if (auto roList = Ptr(obj.Obj()->SafeAggregationCast<IValueReadonlyList>()))
|
|
return SerializeValue(serializer, RpcBoxValueByref(roList->Get(index), lifecycle));
|
|
throw Exception(L"RpcCalleeListOps::ListGet cannot find the target list.");
|
|
}
|
|
|
|
void RpcCalleeListOps::ListSet(RpcObjectReference ref, vint index, const Value& value)
|
|
{
|
|
auto trivial = RpcUnboxValueByref(DeserializeValue(serializer, value), lifecycle);
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
if (auto array = Ptr(obj.Obj()->SafeAggregationCast<IValueArray>()))
|
|
{
|
|
array->Set(index, trivial);
|
|
return;
|
|
}
|
|
if (auto list = Ptr(obj.Obj()->SafeAggregationCast<IValueList>()))
|
|
{
|
|
list->Set(index, trivial);
|
|
return;
|
|
}
|
|
throw Exception(L"RpcCalleeListOps::ListSet cannot find the target list.");
|
|
}
|
|
|
|
vint RpcCalleeListOps::ListAdd(RpcObjectReference ref, const Value& value)
|
|
{
|
|
auto trivial = RpcUnboxValueByref(DeserializeValue(serializer, value), lifecycle);
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
if (auto list = Ptr(obj.Obj()->SafeAggregationCast<IValueList>()))
|
|
return list->Add(trivial);
|
|
throw Exception(L"RpcCalleeListOps::ListAdd cannot find a writable list.");
|
|
}
|
|
|
|
vint RpcCalleeListOps::ListInsert(RpcObjectReference ref, vint index, const Value& value)
|
|
{
|
|
auto trivial = RpcUnboxValueByref(DeserializeValue(serializer, value), lifecycle);
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
if (auto list = Ptr(obj.Obj()->SafeAggregationCast<IValueList>()))
|
|
return list->Insert(index, trivial);
|
|
throw Exception(L"RpcCalleeListOps::ListInsert cannot find a writable list.");
|
|
}
|
|
|
|
bool RpcCalleeListOps::ListRemoveAt(RpcObjectReference ref, vint index)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
if (auto list = Ptr(obj.Obj()->SafeAggregationCast<IValueList>()))
|
|
return list->RemoveAt(index);
|
|
throw Exception(L"RpcCalleeListOps::ListRemoveAt cannot find the target list.");
|
|
}
|
|
|
|
void RpcCalleeListOps::ListClear(RpcObjectReference ref)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
if (auto list = Ptr(obj.Obj()->SafeAggregationCast<IValueList>()))
|
|
{
|
|
list->Clear();
|
|
return;
|
|
}
|
|
throw Exception(L"RpcCalleeListOps::ListClear cannot find the target list.");
|
|
}
|
|
|
|
bool RpcCalleeListOps::ListContains(RpcObjectReference ref, const Value& value)
|
|
{
|
|
auto trivial = RpcUnboxValueByref(DeserializeValue(serializer, value), lifecycle);
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
if (auto roList = Ptr(obj.Obj()->SafeAggregationCast<IValueReadonlyList>()))
|
|
return roList->Contains(trivial);
|
|
throw Exception(L"RpcCalleeListOps::ListContains cannot find the target list.");
|
|
}
|
|
|
|
vint RpcCalleeListOps::ListIndexOf(RpcObjectReference ref, const Value& value)
|
|
{
|
|
auto trivial = RpcUnboxValueByref(DeserializeValue(serializer, value), lifecycle);
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
if (auto roList = Ptr(obj.Obj()->SafeAggregationCast<IValueReadonlyList>()))
|
|
return roList->IndexOf(trivial);
|
|
throw Exception(L"RpcCalleeListOps::ListIndexOf cannot find the target list.");
|
|
}
|
|
|
|
void RpcCalleeListOps::ArrayResize(RpcObjectReference ref, vint size)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
auto array = Ptr(obj.Obj()->SafeAggregationCast<IValueArray>());
|
|
if (!array) throw Exception(L"RpcCalleeListOps::ArrayResize cannot find the target array.");
|
|
array->Resize(size);
|
|
}
|
|
|
|
vint RpcCalleeListOps::DictGetCount(RpcObjectReference ref)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
auto dict = Ptr(obj.Obj()->SafeAggregationCast<IValueReadonlyDictionary>());
|
|
if (!dict) throw Exception(L"RpcCalleeListOps::DictGetCount cannot find the target dictionary.");
|
|
return dict->GetCount();
|
|
}
|
|
|
|
Value RpcCalleeListOps::DictGet(RpcObjectReference ref, const Value& key)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
auto dict = Ptr(obj.Obj()->SafeAggregationCast<IValueReadonlyDictionary>());
|
|
if (!dict) throw Exception(L"RpcCalleeListOps::DictGet cannot find the target dictionary.");
|
|
auto trivialKey = RpcUnboxValueByref(DeserializeValue(serializer, key), lifecycle);
|
|
return SerializeValue(serializer, RpcBoxValueByref(dict->Get(trivialKey), lifecycle));
|
|
}
|
|
|
|
void RpcCalleeListOps::DictSet(RpcObjectReference ref, const Value& key, const Value& value)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
auto dict = Ptr(obj.Obj()->SafeAggregationCast<IValueDictionary>());
|
|
if (!dict) throw Exception(L"RpcCalleeListOps::DictSet cannot find the target dictionary.");
|
|
auto trivialKey = RpcUnboxValueByref(DeserializeValue(serializer, key), lifecycle);
|
|
auto trivialValue = RpcUnboxValueByref(DeserializeValue(serializer, value), lifecycle);
|
|
dict->Set(trivialKey, trivialValue);
|
|
}
|
|
|
|
bool RpcCalleeListOps::DictRemove(RpcObjectReference ref, const Value& key)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
auto dict = Ptr(obj.Obj()->SafeAggregationCast<IValueDictionary>());
|
|
if (!dict) throw Exception(L"RpcCalleeListOps::DictRemove cannot find the target dictionary.");
|
|
return dict->Remove(RpcUnboxValueByref(DeserializeValue(serializer, key), lifecycle));
|
|
}
|
|
|
|
void RpcCalleeListOps::DictClear(RpcObjectReference ref)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
auto dict = Ptr(obj.Obj()->SafeAggregationCast<IValueDictionary>());
|
|
if (!dict) throw Exception(L"RpcCalleeListOps::DictClear cannot find the target dictionary.");
|
|
dict->Clear();
|
|
}
|
|
|
|
bool RpcCalleeListOps::DictContainsKey(RpcObjectReference ref, const Value& key)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
auto dict = Ptr(obj.Obj()->SafeAggregationCast<IValueReadonlyDictionary>());
|
|
if (!dict) throw Exception(L"RpcCalleeListOps::DictContainsKey cannot find the target dictionary.");
|
|
return dict->GetKeys()->Contains(RpcUnboxValueByref(DeserializeValue(serializer, key), lifecycle));
|
|
}
|
|
|
|
RpcObjectReference RpcCalleeListOps::DictGetKeys(RpcObjectReference ref)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
auto dict = Ptr(obj.Obj()->SafeAggregationCast<IValueReadonlyDictionary>());
|
|
if (!dict) throw Exception(L"RpcCalleeListOps::DictGetKeys cannot find the target dictionary.");
|
|
return lifecycle->PtrToRef(dict->GetKeys());
|
|
}
|
|
|
|
RpcObjectReference RpcCalleeListOps::DictGetValues(RpcObjectReference ref)
|
|
{
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
auto dict = Ptr(obj.Obj()->SafeAggregationCast<IValueReadonlyDictionary>());
|
|
if (!dict) throw Exception(L"RpcCalleeListOps::DictGetValues cannot find the target dictionary.");
|
|
return lifecycle->PtrToRef(dict->GetValues());
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcCalleeListEventOps
|
|
***********************************************************************/
|
|
|
|
RpcCalleeListEventOps::RpcCalleeListEventOps(IRpcLifecycle* lc, IRpcSerializer* _serializer)
|
|
: lifecycle(lc)
|
|
, serializer(_serializer)
|
|
{
|
|
if (!lifecycle) CHECK_FAIL(L"Invalid IRpcLifecycle.");
|
|
}
|
|
|
|
Value RpcCalleeListEventOps::OnItemChanged(RpcObjectReference ref, vint index, vint oldCount, vint newCount)
|
|
{
|
|
struct SuppressFlag
|
|
{
|
|
IRpcLifecycle* lifecycle;
|
|
RpcObjectReference ref;
|
|
|
|
SuppressFlag(IRpcLifecycle* _lifecycle, RpcObjectReference _ref)
|
|
: lifecycle(_lifecycle)
|
|
, ref(_ref)
|
|
{
|
|
lifecycle->GetController()->SetItemChangedSuppressedFlag(ref, true);
|
|
}
|
|
~SuppressFlag()
|
|
{
|
|
lifecycle->GetController()->SetItemChangedSuppressedFlag(ref, false);
|
|
}
|
|
};
|
|
|
|
auto obj = lifecycle->RefToPtr(ref);
|
|
auto observable = Ptr(obj.Obj()->SafeAggregationCast<IValueObservableList>());
|
|
CHECK_ERROR(observable, L"RpcCalleeListEventOps::OnItemChanged cannot find the target observable list.");
|
|
|
|
RpcEventExceptionMap exceptions;
|
|
{
|
|
SuppressFlag suppressFlag(lifecycle, ref);
|
|
try
|
|
{
|
|
observable->ItemChanged(index, oldCount, newCount);
|
|
}
|
|
catch (const Exception& ex)
|
|
{
|
|
exceptions = IValueDictionary::Create();
|
|
exceptions->Set(BoxValue(lifecycle->GetClientId()), BoxValue(RpcException{ ex.Message() }));
|
|
}
|
|
}
|
|
return SerializeValue(serializer, BoxValue(exceptions));
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcCalleeObjectOpsForList
|
|
***********************************************************************/
|
|
|
|
RpcCalleeObjectOpsForList::RpcCalleeObjectOpsForList(Ptr<RpcCalleeListOps> _listOps, Ptr<IRpcObjectOps> _objectOps, IRpcSerializer* _serializer)
|
|
: listOps(_listOps)
|
|
, objectOps(_objectOps)
|
|
, serializer(_serializer)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcCalleeObjectOpsForList::RpcCalleeObjectOpsForList(...)#"
|
|
CHECK_ERROR(listOps && objectOps, ERROR_MESSAGE_PREFIX L"List ops and object ops are required.");
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
Value RpcCalleeObjectOpsForList::InvokeMethod(RpcObjectReference ref, vint methodId, Ptr<IValueArray> arguments)
|
|
{
|
|
if (!IsRpcListMethodId(methodId))
|
|
{
|
|
return objectOps->InvokeMethod(ref, methodId, arguments);
|
|
}
|
|
|
|
try
|
|
{
|
|
switch (methodId)
|
|
{
|
|
case RpcMethodId_IValueEnumerable_CreateEnumerator:
|
|
return SerializeValue(serializer, BoxValue(listOps->EnumCreate(ref)));
|
|
case RpcMethodId_IValueEnumerator_Next:
|
|
return SerializeValue(serializer, BoxValue(listOps->EnumNext(ref)));
|
|
case RpcMethodId_IValueEnumerator_GetCurrent:
|
|
return listOps->EnumGetCurrent(ref);
|
|
case RpcMethodId_IValueReadonlyList_GetCount:
|
|
return SerializeValue(serializer, BoxValue(listOps->ListGetCount(ref)));
|
|
case RpcMethodId_IValueReadonlyList_Get:
|
|
return listOps->ListGet(ref, UnboxValue<vint>(DeserializeValue(serializer, arguments->Get(0))));
|
|
case RpcMethodId_IValueList_Set:
|
|
listOps->ListSet(ref, UnboxValue<vint>(DeserializeValue(serializer, arguments->Get(0))), arguments->Get(1));
|
|
return SerializeValue(serializer, Value());
|
|
case RpcMethodId_IValueList_Add:
|
|
return SerializeValue(serializer, BoxValue(listOps->ListAdd(ref, arguments->Get(0))));
|
|
case RpcMethodId_IValueList_Insert:
|
|
return SerializeValue(serializer, BoxValue(listOps->ListInsert(ref, UnboxValue<vint>(DeserializeValue(serializer, arguments->Get(0))), arguments->Get(1))));
|
|
case RpcMethodId_IValueList_RemoveAt:
|
|
return SerializeValue(serializer, BoxValue(listOps->ListRemoveAt(ref, UnboxValue<vint>(DeserializeValue(serializer, arguments->Get(0))))));
|
|
case RpcMethodId_IValueList_Clear:
|
|
listOps->ListClear(ref);
|
|
return SerializeValue(serializer, Value());
|
|
case RpcMethodId_IValueReadonlyList_Contains:
|
|
return SerializeValue(serializer, BoxValue(listOps->ListContains(ref, arguments->Get(0))));
|
|
case RpcMethodId_IValueReadonlyList_IndexOf:
|
|
return SerializeValue(serializer, BoxValue(listOps->ListIndexOf(ref, arguments->Get(0))));
|
|
case RpcMethodId_IValueReadonlyDictionary_GetCount:
|
|
return SerializeValue(serializer, BoxValue(listOps->DictGetCount(ref)));
|
|
case RpcMethodId_IValueReadonlyDictionary_Get:
|
|
return listOps->DictGet(ref, arguments->Get(0));
|
|
case RpcMethodId_IValueDictionary_Set:
|
|
listOps->DictSet(ref, arguments->Get(0), arguments->Get(1));
|
|
return SerializeValue(serializer, Value());
|
|
case RpcMethodId_IValueDictionary_Remove:
|
|
return SerializeValue(serializer, BoxValue(listOps->DictRemove(ref, arguments->Get(0))));
|
|
case RpcMethodId_IValueDictionary_Clear:
|
|
listOps->DictClear(ref);
|
|
return SerializeValue(serializer, Value());
|
|
case RpcMethodId_IValueReadonlyDictionary_ContainsKey:
|
|
return SerializeValue(serializer, BoxValue(listOps->DictContainsKey(ref, arguments->Get(0))));
|
|
case RpcMethodId_IValueReadonlyDictionary_GetKeys:
|
|
return SerializeValue(serializer, BoxValue(listOps->DictGetKeys(ref)));
|
|
case RpcMethodId_IValueReadonlyDictionary_GetValues:
|
|
return SerializeValue(serializer, BoxValue(listOps->DictGetValues(ref)));
|
|
case RpcMethodId_IValueArray_Resize:
|
|
listOps->ArrayResize(ref, UnboxValue<vint>(DeserializeValue(serializer, arguments->Get(0))));
|
|
return SerializeValue(serializer, Value());
|
|
}
|
|
}
|
|
catch (const Exception& ex)
|
|
{
|
|
return SerializeValue(serializer, BoxValue(RpcException{ ex.Message() }));
|
|
}
|
|
CHECK_FAIL(L"Unknown RPC list method id.");
|
|
return {};
|
|
}
|
|
|
|
void RpcCalleeObjectOpsForList::EndInvokeMethod(vint slot)
|
|
{
|
|
objectOps->EndInvokeMethod(slot);
|
|
}
|
|
|
|
void RpcCalleeObjectOpsForList::ObjectHold(RpcObjectReference ref, vint remoteClientId, bool hold)
|
|
{
|
|
objectOps->ObjectHold(ref, remoteClientId, hold);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcCalleeObjectEventOpsForList
|
|
***********************************************************************/
|
|
|
|
RpcCalleeObjectEventOpsForList::RpcCalleeObjectEventOpsForList(Ptr<RpcCalleeListEventOps> _listEventOps, Ptr<IRpcObjectEventOps> _objectEventOps, IRpcSerializer* _serializer)
|
|
: listEventOps(_listEventOps)
|
|
, objectEventOps(_objectEventOps)
|
|
, serializer(_serializer)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcCalleeObjectEventOpsForList::RpcCalleeObjectEventOpsForList(...)#"
|
|
CHECK_ERROR(listEventOps && objectEventOps, ERROR_MESSAGE_PREFIX L"List event ops and object event ops are required.");
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
Value RpcCalleeObjectEventOpsForList::InvokeEvent(RpcObjectReference ref, vint eventId, Ptr<IValueArray> arguments)
|
|
{
|
|
if (eventId == RpcEventId_IValueObservableList_ItemChanged)
|
|
{
|
|
return listEventOps->OnItemChanged(
|
|
ref,
|
|
UnboxValue<vint>(DeserializeValue(serializer, arguments->Get(0))),
|
|
UnboxValue<vint>(DeserializeValue(serializer, arguments->Get(1))),
|
|
UnboxValue<vint>(DeserializeValue(serializer, arguments->Get(2)))
|
|
);
|
|
}
|
|
return objectEventOps->InvokeEvent(ref, eventId, arguments);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcCallerListOps
|
|
***********************************************************************/
|
|
|
|
RpcCallerListOps::RpcCallerListOps(IRpcObjectOps* _objectOps, IRpcSerializer* _serializer)
|
|
: objectOps(_objectOps)
|
|
, serializer(_serializer)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcCallerListOps::RpcCallerListOps(...)#"
|
|
CHECK_ERROR(objectOps, ERROR_MESSAGE_PREFIX L"Object ops are required.");
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
RpcObjectReference RpcCallerListOps::EnumCreate(RpcObjectReference ref)
|
|
{
|
|
auto result = InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueEnumerable_CreateEnumerator, CreateRpcArguments());
|
|
return UnboxValue<RpcObjectReference>(result);
|
|
}
|
|
|
|
bool RpcCallerListOps::EnumNext(RpcObjectReference enumerator)
|
|
{
|
|
auto result = InvokeListMethod(objectOps, serializer, enumerator, RpcMethodId_IValueEnumerator_Next, CreateRpcArguments());
|
|
return UnboxValue<bool>(result);
|
|
}
|
|
|
|
Value RpcCallerListOps::EnumGetCurrent(RpcObjectReference enumerator)
|
|
{
|
|
return InvokeListMethod(objectOps, serializer, enumerator, RpcMethodId_IValueEnumerator_GetCurrent, CreateRpcArguments());
|
|
}
|
|
|
|
vint RpcCallerListOps::ListGetCount(RpcObjectReference ref)
|
|
{
|
|
auto result = InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueReadonlyList_GetCount, CreateRpcArguments());
|
|
return UnboxValue<vint>(result);
|
|
}
|
|
|
|
Value RpcCallerListOps::ListGet(RpcObjectReference ref, vint index)
|
|
{
|
|
return InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueReadonlyList_Get, CreateRpcArguments(SerializeValue(serializer, BoxValue(index))));
|
|
}
|
|
|
|
void RpcCallerListOps::ListSet(RpcObjectReference ref, vint index, const Value& value)
|
|
{
|
|
InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueList_Set, CreateRpcArguments(SerializeValue(serializer, BoxValue(index)), value));
|
|
}
|
|
|
|
vint RpcCallerListOps::ListAdd(RpcObjectReference ref, const Value& value)
|
|
{
|
|
auto result = InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueList_Add, CreateRpcArguments(value));
|
|
return UnboxValue<vint>(result);
|
|
}
|
|
|
|
vint RpcCallerListOps::ListInsert(RpcObjectReference ref, vint index, const Value& value)
|
|
{
|
|
auto result = InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueList_Insert, CreateRpcArguments(SerializeValue(serializer, BoxValue(index)), value));
|
|
return UnboxValue<vint>(result);
|
|
}
|
|
|
|
bool RpcCallerListOps::ListRemoveAt(RpcObjectReference ref, vint index)
|
|
{
|
|
auto result = InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueList_RemoveAt, CreateRpcArguments(SerializeValue(serializer, BoxValue(index))));
|
|
return UnboxValue<bool>(result);
|
|
}
|
|
|
|
void RpcCallerListOps::ListClear(RpcObjectReference ref)
|
|
{
|
|
InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueList_Clear, CreateRpcArguments());
|
|
}
|
|
|
|
bool RpcCallerListOps::ListContains(RpcObjectReference ref, const Value& value)
|
|
{
|
|
auto result = InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueReadonlyList_Contains, CreateRpcArguments(value));
|
|
return UnboxValue<bool>(result);
|
|
}
|
|
|
|
vint RpcCallerListOps::ListIndexOf(RpcObjectReference ref, const Value& value)
|
|
{
|
|
auto result = InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueReadonlyList_IndexOf, CreateRpcArguments(value));
|
|
return UnboxValue<vint>(result);
|
|
}
|
|
|
|
void RpcCallerListOps::ArrayResize(RpcObjectReference ref, vint size)
|
|
{
|
|
InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueArray_Resize, CreateRpcArguments(SerializeValue(serializer, BoxValue(size))));
|
|
}
|
|
|
|
vint RpcCallerListOps::DictGetCount(RpcObjectReference ref)
|
|
{
|
|
auto result = InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueReadonlyDictionary_GetCount, CreateRpcArguments());
|
|
return UnboxValue<vint>(result);
|
|
}
|
|
|
|
Value RpcCallerListOps::DictGet(RpcObjectReference ref, const Value& key)
|
|
{
|
|
return InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueReadonlyDictionary_Get, CreateRpcArguments(key));
|
|
}
|
|
|
|
void RpcCallerListOps::DictSet(RpcObjectReference ref, const Value& key, const Value& value)
|
|
{
|
|
InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueDictionary_Set, CreateRpcArguments(key, value));
|
|
}
|
|
|
|
bool RpcCallerListOps::DictRemove(RpcObjectReference ref, const Value& key)
|
|
{
|
|
auto result = InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueDictionary_Remove, CreateRpcArguments(key));
|
|
return UnboxValue<bool>(result);
|
|
}
|
|
|
|
void RpcCallerListOps::DictClear(RpcObjectReference ref)
|
|
{
|
|
InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueDictionary_Clear, CreateRpcArguments());
|
|
}
|
|
|
|
bool RpcCallerListOps::DictContainsKey(RpcObjectReference ref, const Value& key)
|
|
{
|
|
auto result = InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueReadonlyDictionary_ContainsKey, CreateRpcArguments(key));
|
|
return UnboxValue<bool>(result);
|
|
}
|
|
|
|
RpcObjectReference RpcCallerListOps::DictGetKeys(RpcObjectReference ref)
|
|
{
|
|
auto result = InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueReadonlyDictionary_GetKeys, CreateRpcArguments());
|
|
return UnboxValue<RpcObjectReference>(result);
|
|
}
|
|
|
|
RpcObjectReference RpcCallerListOps::DictGetValues(RpcObjectReference ref)
|
|
{
|
|
auto result = InvokeListMethod(objectOps, serializer, ref, RpcMethodId_IValueReadonlyDictionary_GetValues, CreateRpcArguments());
|
|
return UnboxValue<RpcObjectReference>(result);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcCallerListEventOps
|
|
***********************************************************************/
|
|
|
|
RpcCallerListEventOps::RpcCallerListEventOps(IRpcObjectEventOps* _objectEventOps, IRpcSerializer* _serializer)
|
|
: objectEventOps(_objectEventOps)
|
|
, serializer(_serializer)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcCallerListEventOps::RpcCallerListEventOps(...)#"
|
|
CHECK_ERROR(objectEventOps, ERROR_MESSAGE_PREFIX L"Object event ops are required.");
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
Value RpcCallerListEventOps::OnItemChanged(RpcObjectReference ref, vint index, vint oldCount, vint newCount)
|
|
{
|
|
auto arguments = CreateRpcArguments(
|
|
SerializeValue(serializer, BoxValue(index)),
|
|
SerializeValue(serializer, BoxValue(oldCount)),
|
|
SerializeValue(serializer, BoxValue(newCount))
|
|
);
|
|
auto result = objectEventOps->InvokeEvent(ref, RpcEventId_IValueObservableList_ItemChanged, arguments);
|
|
ReadEventException(UnboxValue<RpcEventExceptionMap>(DeserializeValue(serializer, result)));
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
.\RPCJSON\WFLIBRARYRPCJSON.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace rpc_controller
|
|
{
|
|
using namespace collections;
|
|
using namespace glr::json;
|
|
using namespace reflection;
|
|
using namespace reflection::description;
|
|
|
|
namespace
|
|
{
|
|
Ptr<JsonLiteral> CreateJsonLiteral(JsonLiteralValue value)
|
|
{
|
|
auto node = Ptr(new JsonLiteral);
|
|
node->value = value;
|
|
return node;
|
|
}
|
|
|
|
Ptr<JsonString> CreateJsonString(const WString& value)
|
|
{
|
|
auto node = Ptr(new JsonString);
|
|
node->content.value = value;
|
|
return node;
|
|
}
|
|
|
|
Ptr<JsonNumber> CreateJsonNumber(const WString& value)
|
|
{
|
|
auto node = Ptr(new JsonNumber);
|
|
node->content.value = value;
|
|
return node;
|
|
}
|
|
|
|
Ptr<JsonArray> CreateJsonArray()
|
|
{
|
|
return Ptr(new JsonArray);
|
|
}
|
|
|
|
Ptr<JsonObject> CreateJsonObject()
|
|
{
|
|
return Ptr(new JsonObject);
|
|
}
|
|
|
|
Ptr<JsonObject> GetJsonObject(Ptr<JsonNode> node)
|
|
{
|
|
auto object = node.Cast<JsonObject>();
|
|
CHECK_ERROR(object, L"JSON object is expected.");
|
|
return object;
|
|
}
|
|
|
|
Ptr<JsonArray> GetJsonArray(Ptr<JsonNode> node)
|
|
{
|
|
auto array = node.Cast<JsonArray>();
|
|
CHECK_ERROR(array, L"JSON array is expected.");
|
|
return array;
|
|
}
|
|
|
|
void AddJsonObjectField(Ptr<JsonObject> object, const WString& name, Ptr<JsonNode> value)
|
|
{
|
|
auto field = Ptr(new JsonObjectField);
|
|
field->name.value = name;
|
|
field->value = value;
|
|
object->fields.Add(field);
|
|
}
|
|
|
|
void SetJsonObjectField(Ptr<JsonObject> object, const WString& name, Ptr<JsonNode> value)
|
|
{
|
|
for (auto field : object->fields)
|
|
{
|
|
if (field->name.value == name)
|
|
{
|
|
field->value = value;
|
|
return;
|
|
}
|
|
}
|
|
AddJsonObjectField(object, name, value);
|
|
}
|
|
|
|
Ptr<JsonNode> GetJsonObjectField(Ptr<JsonObject> object, const WString& name)
|
|
{
|
|
for (auto field : object->fields)
|
|
{
|
|
if (field->name.value == name)
|
|
{
|
|
return field->value;
|
|
}
|
|
}
|
|
auto message = L"JSON object field not found: " + name;
|
|
CHECK_FAIL(message.Buffer());
|
|
}
|
|
|
|
Ptr<JsonNode> FindJsonObjectField(Ptr<JsonObject> object, const WString& name)
|
|
{
|
|
for (auto field : object->fields)
|
|
{
|
|
if (field->name.value == name)
|
|
{
|
|
return field->value;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
WString GetJsonString(Ptr<JsonNode> node)
|
|
{
|
|
auto stringNode = node.Cast<JsonString>();
|
|
CHECK_ERROR(stringNode, L"JSON string is expected.");
|
|
return stringNode->content.value;
|
|
}
|
|
|
|
WString GetJsonNumber(Ptr<JsonNode> node)
|
|
{
|
|
auto numberNode = node.Cast<JsonNumber>();
|
|
CHECK_ERROR(numberNode, L"JSON number is expected.");
|
|
return numberNode->content.value;
|
|
}
|
|
|
|
vint GetJsonInt(Ptr<JsonNode> node)
|
|
{
|
|
return __vwsn::Parse<vint>(GetJsonNumber(node));
|
|
}
|
|
|
|
bool GetJsonBool(Ptr<JsonNode> node)
|
|
{
|
|
auto literal = node.Cast<JsonLiteral>();
|
|
CHECK_ERROR(literal, L"JSON boolean is expected.");
|
|
if (literal->value == JsonLiteralValue::True) return true;
|
|
if (literal->value == JsonLiteralValue::False) return false;
|
|
CHECK_FAIL(L"JSON boolean is expected.");
|
|
return false;
|
|
}
|
|
|
|
Ptr<JsonNumber> CreateJsonNumber(vint value)
|
|
{
|
|
return CreateJsonNumber(__vwsn::ToString(value));
|
|
}
|
|
|
|
Ptr<JsonLiteral> CreateJsonBool(bool value)
|
|
{
|
|
return CreateJsonLiteral(value ? JsonLiteralValue::True : JsonLiteralValue::False);
|
|
}
|
|
|
|
Ptr<JsonNode> CreateUnknownTuple(const WString& keyword, Ptr<JsonNode> value)
|
|
{
|
|
auto array = CreateJsonArray();
|
|
array->items.Add(CreateJsonString(keyword));
|
|
array->items.Add(value);
|
|
return Ptr<JsonNode>(array);
|
|
}
|
|
|
|
template<typename T>
|
|
bool TrySerializeNumberPrimitive(const Value& value, const WString& keyword, Ptr<JsonNode>& result)
|
|
{
|
|
auto primitive = __vwsn::UnboxWeak<Nullable<T>>(value);
|
|
if (primitive)
|
|
{
|
|
result = CreateUnknownTuple(keyword, CreateJsonNumber(__vwsn::ToString(primitive.Value())));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<typename T>
|
|
bool TrySerializeStringPrimitive(const Value& value, const WString& keyword, Ptr<JsonNode>& result)
|
|
{
|
|
auto primitive = __vwsn::UnboxWeak<Nullable<T>>(value);
|
|
if (primitive)
|
|
{
|
|
result = CreateUnknownTuple(keyword, CreateJsonString(__vwsn::ToString(primitive.Value())));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Ptr<JsonNode> SerializeCollection(const WString& keyword, Ptr<IValueEnumerable> enumerable, const RpcJsonSerializeCallback& rpcjson_Serialize)
|
|
{
|
|
auto object = CreateJsonObject();
|
|
AddJsonObjectField(object, WString::Unmanaged(L"$"), CreateJsonString(keyword));
|
|
auto values = CreateJsonArray();
|
|
auto enumerator = enumerable->CreateEnumerator();
|
|
while (enumerator->Next())
|
|
{
|
|
values->items.Add(rpcjson_Serialize(enumerator->GetCurrent()));
|
|
}
|
|
AddJsonObjectField(object, WString::Unmanaged(L"values"), values);
|
|
return Ptr<JsonNode>(object);
|
|
}
|
|
|
|
Ptr<JsonNode> SerializeMap(Ptr<IValueReadonlyDictionary> dictionary, const RpcJsonSerializeCallback& rpcjson_Serialize)
|
|
{
|
|
auto object = CreateJsonObject();
|
|
AddJsonObjectField(object, WString::Unmanaged(L"$"), CreateJsonString(WString::Unmanaged(L"map")));
|
|
auto values = CreateJsonArray();
|
|
auto keys = dictionary->GetKeys();
|
|
for (vint i = 0; i < keys->GetCount(); i++)
|
|
{
|
|
auto key = keys->Get(i);
|
|
auto pair = CreateJsonArray();
|
|
pair->items.Add(rpcjson_Serialize(key));
|
|
pair->items.Add(rpcjson_Serialize(dictionary->Get(key)));
|
|
values->items.Add(pair);
|
|
}
|
|
AddJsonObjectField(object, WString::Unmanaged(L"values"), values);
|
|
return Ptr<JsonNode>(object);
|
|
}
|
|
|
|
Ptr<JsonNode> TrySerializeCollectionTypes(const Value& value, const RpcJsonSerializeCallback& rpcjson_Serialize)
|
|
{
|
|
if (auto observableList = __vwsn::UnboxWeak<Ptr<IValueObservableList>>(value))
|
|
{
|
|
return SerializeCollection(WString::Unmanaged(L"oblist"), observableList, rpcjson_Serialize);
|
|
}
|
|
if (auto dictionary = __vwsn::UnboxWeak<Ptr<IValueDictionary>>(value))
|
|
{
|
|
return SerializeMap(dictionary, rpcjson_Serialize);
|
|
}
|
|
if (auto readonlyDictionary = __vwsn::UnboxWeak<Ptr<IValueReadonlyDictionary>>(value))
|
|
{
|
|
return SerializeMap(readonlyDictionary, rpcjson_Serialize);
|
|
}
|
|
if (auto list = __vwsn::UnboxWeak<Ptr<IValueList>>(value))
|
|
{
|
|
return SerializeCollection(WString::Unmanaged(L"list"), list, rpcjson_Serialize);
|
|
}
|
|
if (auto readonlyList = __vwsn::UnboxWeak<Ptr<IValueReadonlyList>>(value))
|
|
{
|
|
return SerializeCollection(WString::Unmanaged(L"list"), readonlyList, rpcjson_Serialize);
|
|
}
|
|
if (auto enumerable = __vwsn::UnboxWeak<Ptr<IValueEnumerable>>(value))
|
|
{
|
|
return SerializeCollection(WString::Unmanaged(L"list"), enumerable, rpcjson_Serialize);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Ptr<JsonNode> TrySerializeRpcTypes(const Value& value)
|
|
{
|
|
if (auto ref = __vwsn::UnboxWeak<Nullable<RpcObjectReference>>(value))
|
|
{
|
|
auto object = CreateJsonObject();
|
|
AddJsonObjectField(object, WString::Unmanaged(L"$"), CreateJsonString(WString::Unmanaged(L"system::RpcObjectReference")));
|
|
AddJsonObjectField(object, WString::Unmanaged(L"clientId"), CreateJsonNumber(__vwsn::ToString(ref.Value().clientId)));
|
|
AddJsonObjectField(object, WString::Unmanaged(L"objectId"), CreateJsonNumber(__vwsn::ToString(ref.Value().objectId)));
|
|
AddJsonObjectField(object, WString::Unmanaged(L"typeId"), CreateJsonNumber(__vwsn::ToString(ref.Value().typeId)));
|
|
return Ptr<JsonNode>(object);
|
|
}
|
|
if (auto exception = __vwsn::UnboxWeak<Nullable<RpcException>>(value))
|
|
{
|
|
auto object = CreateJsonObject();
|
|
AddJsonObjectField(object, WString::Unmanaged(L"$"), CreateJsonString(WString::Unmanaged(L"system::RpcException")));
|
|
AddJsonObjectField(object, WString::Unmanaged(L"message"), CreateJsonString(exception.Value().message));
|
|
return Ptr<JsonNode>(object);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Value DeserializeList(Ptr<JsonObject> object, bool observable, const RpcJsonDeserializeCallback& rpcjson_Deserialize)
|
|
{
|
|
auto values = GetJsonObjectField(object, WString::Unmanaged(L"values")).Cast<JsonArray>();
|
|
CHECK_ERROR(values, L"JSON array is expected.");
|
|
if (observable)
|
|
{
|
|
auto result = IValueObservableList::Create();
|
|
for (auto item : values->items)
|
|
{
|
|
result->Add(rpcjson_Deserialize(item));
|
|
}
|
|
return BoxValue(result);
|
|
}
|
|
else
|
|
{
|
|
auto result = IValueList::Create();
|
|
for (auto item : values->items)
|
|
{
|
|
result->Add(rpcjson_Deserialize(item));
|
|
}
|
|
return BoxValue(result);
|
|
}
|
|
}
|
|
|
|
Value DeserializeMap(Ptr<JsonObject> object, const RpcJsonDeserializeCallback& rpcjson_Deserialize)
|
|
{
|
|
auto values = GetJsonObjectField(object, WString::Unmanaged(L"values")).Cast<JsonArray>();
|
|
CHECK_ERROR(values, L"JSON array is expected.");
|
|
auto result = IValueDictionary::Create();
|
|
for (auto item : values->items)
|
|
{
|
|
auto pair = item.Cast<JsonArray>();
|
|
CHECK_ERROR(pair, L"JSON array is expected.");
|
|
result->Set(rpcjson_Deserialize(pair->items[0]), rpcjson_Deserialize(pair->items[1]));
|
|
}
|
|
return BoxValue(result);
|
|
}
|
|
|
|
Value TryDeserializeRpcTypes(Ptr<JsonObject> object, const WString& keyword)
|
|
{
|
|
if (keyword == L"system::RpcObjectReference")
|
|
{
|
|
return BoxValue(RpcObjectReference{
|
|
__vwsn::Parse<vint>(GetJsonNumber(GetJsonObjectField(object, WString::Unmanaged(L"clientId")))),
|
|
__vwsn::Parse<vint>(GetJsonNumber(GetJsonObjectField(object, WString::Unmanaged(L"objectId")))),
|
|
__vwsn::Parse<vint>(GetJsonNumber(GetJsonObjectField(object, WString::Unmanaged(L"typeId")))),
|
|
});
|
|
}
|
|
if (keyword == L"system::RpcException")
|
|
{
|
|
return BoxValue(RpcException{
|
|
GetJsonString(GetJsonObjectField(object, WString::Unmanaged(L"message"))),
|
|
});
|
|
}
|
|
return {};
|
|
}
|
|
|
|
RpcObjectReference GetRpcObjectReferenceFromJson(Ptr<JsonNode> node)
|
|
{
|
|
auto object = GetJsonObject(node);
|
|
return {
|
|
GetJsonInt(GetJsonObjectField(object, WString::Unmanaged(L"clientId"))),
|
|
GetJsonInt(GetJsonObjectField(object, WString::Unmanaged(L"objectId"))),
|
|
GetJsonInt(GetJsonObjectField(object, WString::Unmanaged(L"typeId"))),
|
|
};
|
|
}
|
|
|
|
Ptr<JsonObject> CreateRpcObjectReferenceJson(RpcObjectReference ref)
|
|
{
|
|
auto object = CreateJsonObject();
|
|
AddJsonObjectField(object, WString::Unmanaged(L"clientId"), CreateJsonNumber(ref.clientId));
|
|
AddJsonObjectField(object, WString::Unmanaged(L"objectId"), CreateJsonNumber(ref.objectId));
|
|
AddJsonObjectField(object, WString::Unmanaged(L"typeId"), CreateJsonNumber(ref.typeId));
|
|
return object;
|
|
}
|
|
|
|
Ptr<JsonNode> ValueToJsonNode(const Value& value)
|
|
{
|
|
if (value.GetValueType() == Value::SharedPtr)
|
|
{
|
|
if (auto node = value.GetSharedPtr().Cast<JsonNode>())
|
|
{
|
|
return node;
|
|
}
|
|
}
|
|
CHECK_FAIL(L"RPC JSON value should be a Ptr<JsonNode>.");
|
|
return nullptr;
|
|
}
|
|
|
|
Ptr<JsonArray> ValueArrayToJsonArray(Ptr<IValueArray> arguments)
|
|
{
|
|
auto array = CreateJsonArray();
|
|
for (vint i = 0; i < arguments->GetCount(); i++)
|
|
{
|
|
array->items.Add(ValueToJsonNode(arguments->Get(i)));
|
|
}
|
|
return array;
|
|
}
|
|
|
|
Ptr<IValueArray> JsonArrayToValueArray(Ptr<JsonNode> node)
|
|
{
|
|
auto array = GetJsonArray(node);
|
|
auto arguments = IValueArray::Create();
|
|
arguments->Resize(array->items.Count());
|
|
for (vint i = 0; i < array->items.Count(); i++)
|
|
{
|
|
arguments->Set(i, BoxValue(array->items[i]));
|
|
}
|
|
return arguments;
|
|
}
|
|
|
|
Ptr<JsonObject> CreateRpcMessage(const WString& method, vint requestId, vint sourceClientId)
|
|
{
|
|
auto object = CreateJsonObject();
|
|
AddJsonObjectField(object, WString::Unmanaged(L"rpcMethod"), CreateJsonString(method));
|
|
AddJsonObjectField(object, WString::Unmanaged(L"rpcRequestId"), CreateJsonNumber(requestId));
|
|
AddJsonObjectField(object, WString::Unmanaged(L"sourceClientId"), CreateJsonNumber(sourceClientId));
|
|
return object;
|
|
}
|
|
|
|
vint ReadSourceClientId(Ptr<JsonObject> object)
|
|
{
|
|
return GetJsonInt(GetJsonObjectField(object, WString::Unmanaged(L"sourceClientId")));
|
|
}
|
|
|
|
vint ReadTargetClientId(Ptr<JsonObject> object)
|
|
{
|
|
return GetJsonInt(GetJsonObjectField(object, WString::Unmanaged(L"targetClientId")));
|
|
}
|
|
|
|
bool IsRpcMethodPrefix(const WString& rpcMethod, const WString& prefix)
|
|
{
|
|
return rpcMethod.Length() >= prefix.Length() && rpcMethod.Left(prefix.Length()) == prefix;
|
|
}
|
|
|
|
Ptr<JsonNode> MethodResultToJsonResponse(const Value& result)
|
|
{
|
|
if (result.GetValueType() == Value::SharedPtr)
|
|
{
|
|
if (auto byvalReturnValue = result.GetSharedPtr().Cast<RpcByvalReturnValue>())
|
|
{
|
|
auto object = CreateJsonObject();
|
|
AddJsonObjectField(object, WString::Unmanaged(L"value"), ValueToJsonNode(byvalReturnValue->value));
|
|
AddJsonObjectField(object, WString::Unmanaged(L"slot"), CreateJsonNumber(byvalReturnValue->slot));
|
|
return object;
|
|
}
|
|
}
|
|
return ValueToJsonNode(result);
|
|
}
|
|
|
|
Value JsonResponseToMethodResult(Ptr<JsonNode> node)
|
|
{
|
|
if (auto object = node.Cast<JsonObject>())
|
|
{
|
|
auto valueNode = FindJsonObjectField(object, WString::Unmanaged(L"value"));
|
|
auto slotNode = FindJsonObjectField(object, WString::Unmanaged(L"slot"));
|
|
if (valueNode && slotNode)
|
|
{
|
|
auto byvalReturnValue = Ptr(new RpcByvalReturnValue);
|
|
byvalReturnValue->value = BoxValue(valueNode);
|
|
byvalReturnValue->slot = GetJsonInt(slotNode);
|
|
return BoxValue(byvalReturnValue);
|
|
}
|
|
}
|
|
return BoxValue(node);
|
|
}
|
|
|
|
Ptr<JsonNode> SerializePredefinedValue(const Value& value)
|
|
{
|
|
RpcJsonSerializeCallback rpcjson_Serialize;
|
|
rpcjson_Serialize = Func<Ptr<JsonNode>(const Value&)>([&rpcjson_Serialize](const Value& item)
|
|
{
|
|
return JsonSerializePredefinedTypes(item, rpcjson_Serialize);
|
|
});
|
|
return JsonSerializePredefinedTypes(value, rpcjson_Serialize);
|
|
}
|
|
|
|
Value DeserializePredefinedValue(Ptr<JsonNode> node)
|
|
{
|
|
RpcJsonDeserializeCallback rpcjson_Deserialize;
|
|
rpcjson_Deserialize = Func<Value(Ptr<JsonNode>)>([&rpcjson_Deserialize](Ptr<JsonNode> item)
|
|
{
|
|
return JsonDeserializePredefinedTypes(BoxValue(item), rpcjson_Deserialize);
|
|
});
|
|
return JsonDeserializePredefinedTypes(BoxValue(node), rpcjson_Deserialize);
|
|
}
|
|
|
|
Ptr<JsonObject> CreatePlainRpcException(const RpcException& exception)
|
|
{
|
|
auto result = CreateJsonObject();
|
|
AddJsonObjectField(result, WString::Unmanaged(L"message"), CreateJsonString(exception.message));
|
|
return result;
|
|
}
|
|
|
|
RpcException ReadPlainRpcException(Ptr<JsonNode> node)
|
|
{
|
|
auto object = GetJsonObject(node);
|
|
return { GetJsonString(GetJsonObjectField(object, WString::Unmanaged(L"message"))) };
|
|
}
|
|
|
|
Ptr<JsonNode> CreateEventExceptionResponse(Ptr<JsonNode> serialized)
|
|
{
|
|
auto value = DeserializePredefinedValue(serialized);
|
|
if (value.IsNull())
|
|
{
|
|
return CreateJsonLiteral(JsonLiteralValue::Null);
|
|
}
|
|
|
|
auto exceptions = UnboxValue<RpcEventExceptionMap>(value);
|
|
auto result = CreateJsonArray();
|
|
auto keys = exceptions->GetKeys();
|
|
for (vint i = 0; i < keys->GetCount(); i++)
|
|
{
|
|
auto key = keys->Get(i);
|
|
auto resultPair = CreateJsonArray();
|
|
resultPair->items.Add(CreateJsonNumber(UnboxValue<vint>(key)));
|
|
resultPair->items.Add(CreatePlainRpcException(UnboxValue<RpcException>(exceptions->Get(key))));
|
|
result->items.Add(resultPair);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Ptr<JsonNode> CreateSerializedEventExceptionMap(Ptr<JsonNode> response)
|
|
{
|
|
if (auto literal = response.Cast<JsonLiteral>())
|
|
{
|
|
CHECK_ERROR(literal->value == JsonLiteralValue::Null, L"RPC event exception response should be null or an array.");
|
|
return SerializePredefinedValue({});
|
|
}
|
|
|
|
auto array = GetJsonArray(response);
|
|
auto exceptions = IValueDictionary::Create();
|
|
for (auto item : array->items)
|
|
{
|
|
auto pair = GetJsonArray(item);
|
|
CHECK_ERROR(pair->items.Count() == 2, L"RPC event exception response pair is expected.");
|
|
exceptions->Set(BoxValue(GetJsonInt(pair->items[0])), BoxValue(ReadPlainRpcException(pair->items[1])));
|
|
}
|
|
return SerializePredefinedValue(BoxValue(exceptions));
|
|
}
|
|
}
|
|
|
|
Ptr<JsonNode> JsonSerializePredefinedTypes(const Value& value, const RpcJsonSerializeCallback& rpcjson_Serialize)
|
|
{
|
|
if (value.IsNull())
|
|
{
|
|
return CreateJsonLiteral(JsonLiteralValue::Null);
|
|
}
|
|
|
|
if (auto primitive = __vwsn::UnboxWeak<Nullable<bool>>(value))
|
|
{
|
|
return CreateJsonLiteral(primitive.Value() ? JsonLiteralValue::True : JsonLiteralValue::False);
|
|
}
|
|
|
|
if (auto primitive = __vwsn::UnboxWeak<Nullable<WString>>(value))
|
|
{
|
|
return CreateJsonString(primitive.Value());
|
|
}
|
|
|
|
Ptr<JsonNode> result;
|
|
if (TrySerializeNumberPrimitive<vuint8_t>(value, WString::Unmanaged(L"UInt8"), result)) return result;
|
|
if (TrySerializeNumberPrimitive<vuint16_t>(value, WString::Unmanaged(L"UInt16"), result)) return result;
|
|
if (TrySerializeNumberPrimitive<vuint32_t>(value, WString::Unmanaged(L"UInt32"), result)) return result;
|
|
if (TrySerializeNumberPrimitive<vuint64_t>(value, WString::Unmanaged(L"UInt64"), result)) return result;
|
|
if (TrySerializeNumberPrimitive<vint8_t>(value, WString::Unmanaged(L"Int8"), result)) return result;
|
|
if (TrySerializeNumberPrimitive<vint16_t>(value, WString::Unmanaged(L"Int16"), result)) return result;
|
|
if (TrySerializeNumberPrimitive<vint32_t>(value, WString::Unmanaged(L"Int32"), result)) return result;
|
|
if (TrySerializeNumberPrimitive<vint64_t>(value, WString::Unmanaged(L"Int64"), result)) return result;
|
|
if (TrySerializeNumberPrimitive<float>(value, WString::Unmanaged(L"Single"), result)) return result;
|
|
if (TrySerializeNumberPrimitive<double>(value, WString::Unmanaged(L"Double"), result)) return result;
|
|
if (TrySerializeStringPrimitive<wchar_t>(value, WString::Unmanaged(L"Char"), result)) return result;
|
|
if (TrySerializeStringPrimitive<DateTime>(value, WString::Unmanaged(L"DateTime"), result)) return result;
|
|
if (TrySerializeStringPrimitive<Locale>(value, WString::Unmanaged(L"Locale"), result)) return result;
|
|
|
|
if (auto rpcType = TrySerializeRpcTypes(value))
|
|
{
|
|
return rpcType;
|
|
}
|
|
|
|
if (auto collection = TrySerializeCollectionTypes(value, rpcjson_Serialize))
|
|
{
|
|
return collection;
|
|
}
|
|
|
|
CHECK_FAIL(L"Unsupported RPC JSON serialization value.");
|
|
}
|
|
|
|
Value JsonDeserializePredefinedTypes(const Value& value, const RpcJsonDeserializeCallback& rpcjson_Deserialize)
|
|
{
|
|
if (value.IsNull())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
auto node = UnboxValue<Ptr<JsonNode>>(value);
|
|
if (!node)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (auto literal = node.Cast<JsonLiteral>())
|
|
{
|
|
switch (literal->value)
|
|
{
|
|
case JsonLiteralValue::Null:
|
|
return {};
|
|
case JsonLiteralValue::True:
|
|
return BoxValue(true);
|
|
case JsonLiteralValue::False:
|
|
return BoxValue(false);
|
|
default:
|
|
CHECK_FAIL(L"Unsupported JSON literal.");
|
|
}
|
|
}
|
|
|
|
if (auto stringNode = node.Cast<JsonString>())
|
|
{
|
|
return BoxValue(stringNode->content.value);
|
|
}
|
|
|
|
if (auto numberNode = node.Cast<JsonNumber>())
|
|
{
|
|
return BoxValue(numberNode->content.value);
|
|
}
|
|
|
|
if (auto array = node.Cast<JsonArray>())
|
|
{
|
|
if (array->items.Count() == 2)
|
|
{
|
|
if (auto keywordNode = array->items[0].Cast<JsonString>())
|
|
{
|
|
auto keyword = keywordNode->content.value;
|
|
auto item = array->items[1];
|
|
if (keyword == L"UInt8") return BoxValue(__vwsn::Parse<vuint8_t>(GetJsonNumber(item)));
|
|
if (keyword == L"UInt16") return BoxValue(__vwsn::Parse<vuint16_t>(GetJsonNumber(item)));
|
|
if (keyword == L"UInt32") return BoxValue(__vwsn::Parse<vuint32_t>(GetJsonNumber(item)));
|
|
if (keyword == L"UInt64") return BoxValue(__vwsn::Parse<vuint64_t>(GetJsonNumber(item)));
|
|
if (keyword == L"Int8") return BoxValue(__vwsn::Parse<vint8_t>(GetJsonNumber(item)));
|
|
if (keyword == L"Int16") return BoxValue(__vwsn::Parse<vint16_t>(GetJsonNumber(item)));
|
|
if (keyword == L"Int32") return BoxValue(__vwsn::Parse<vint32_t>(GetJsonNumber(item)));
|
|
if (keyword == L"Int64") return BoxValue(__vwsn::Parse<vint64_t>(GetJsonNumber(item)));
|
|
if (keyword == L"Single") return BoxValue(__vwsn::Parse<float>(GetJsonNumber(item)));
|
|
if (keyword == L"Double") return BoxValue(__vwsn::Parse<double>(GetJsonNumber(item)));
|
|
if (keyword == L"Char") return BoxValue(__vwsn::Parse<wchar_t>(GetJsonString(item)));
|
|
if (keyword == L"DateTime") return BoxValue(__vwsn::Parse<DateTime>(GetJsonString(item)));
|
|
if (keyword == L"Locale") return BoxValue(__vwsn::Parse<Locale>(GetJsonString(item)));
|
|
}
|
|
}
|
|
|
|
auto result = IValueList::Create();
|
|
for (auto item : array->items)
|
|
{
|
|
result->Add(rpcjson_Deserialize(item));
|
|
}
|
|
return BoxValue(result);
|
|
}
|
|
|
|
if (auto object = node.Cast<JsonObject>())
|
|
{
|
|
if (auto keywordNode = FindJsonObjectField(object, WString::Unmanaged(L"$")))
|
|
{
|
|
auto keyword = GetJsonString(keywordNode);
|
|
if (auto value = TryDeserializeRpcTypes(object, keyword); !value.IsNull()) return value;
|
|
if (keyword == L"list") return DeserializeList(object, false, rpcjson_Deserialize);
|
|
if (keyword == L"oblist") return DeserializeList(object, true, rpcjson_Deserialize);
|
|
if (keyword == L"map") return DeserializeMap(object, rpcjson_Deserialize);
|
|
CHECK_FAIL(L"Unknown RPC JSON object schema.");
|
|
}
|
|
|
|
auto result = IValueDictionary::Create();
|
|
for (auto field : object->fields)
|
|
{
|
|
result->Set(BoxValue(field->name.value), rpcjson_Deserialize(field->value));
|
|
}
|
|
return BoxValue(result);
|
|
}
|
|
|
|
CHECK_FAIL(L"Unsupported RPC JSON node.");
|
|
}
|
|
|
|
/***********************************************************************
|
|
* IRpcJsonMessageDispatcher
|
|
***********************************************************************/
|
|
|
|
Ptr<JsonNode> IRpcJsonMessageDispatcher::DefaultTranslate(
|
|
Ptr<JsonNode> message,
|
|
RequestType requestType,
|
|
IRpcObjectOps* objectOps,
|
|
IRpcObjectEventOps* objectEventOps,
|
|
IRpcDispatcher* dispatcher,
|
|
IRpcLifecycle* lifecycle
|
|
)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::IRpcJsonMessageDispatcher::DefaultTranslate(Ptr<JsonNode>, RequestType, IRpcObjectOps*, IRpcObjectEventOps*, IRpcDispatcher*, IRpcLifecycle*)#"
|
|
auto request = GetJsonObject(message);
|
|
auto rpcMethod = GetJsonString(GetJsonObjectField(request, WString::Unmanaged(L"rpcMethod")));
|
|
|
|
if (requestType == RequestType::Direct && IsRpcMethodPrefix(rpcMethod, WString::Unmanaged(L"Request:IObjectOps_")))
|
|
{
|
|
return RpcJsonObjectOps::Translate(message, objectOps, lifecycle);
|
|
}
|
|
else if (requestType == RequestType::Broadcast && IsRpcMethodPrefix(rpcMethod, WString::Unmanaged(L"Request:IObjectEventOps_")))
|
|
{
|
|
return RpcJsonObjectEventOps::Translate(message, objectEventOps, lifecycle);
|
|
}
|
|
else if (requestType == RequestType::BroadcastAndDrop && rpcMethod == WString::Unmanaged(L"Request:IRpcDispatcher_DeclareRemoteService"))
|
|
{
|
|
RpcJsonDispatcher::Translate(message, dispatcher, lifecycle);
|
|
return nullptr;
|
|
}
|
|
|
|
CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unknown JSON RPC method.");
|
|
return nullptr;
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcJsonObjectOps
|
|
***********************************************************************/
|
|
|
|
RpcJsonObjectOps::RpcJsonObjectOps(IRpcJsonMessageDispatcher* _dispatcher)
|
|
: dispatcher(_dispatcher)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonObjectOps::RpcJsonObjectOps(...)#"
|
|
CHECK_ERROR(dispatcher, ERROR_MESSAGE_PREFIX L"Dispatcher is required.");
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
RpcJsonObjectOps::RpcJsonObjectOps(vint _sourceClientId, vint _targetClientId, IRpcJsonMessageDispatcher* _dispatcher)
|
|
: sourceClientId(_sourceClientId)
|
|
, targetClientId(_targetClientId)
|
|
, dispatcher(_dispatcher)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonObjectOps::RpcJsonObjectOps(...)#"
|
|
CHECK_ERROR(dispatcher, ERROR_MESSAGE_PREFIX L"Dispatcher is required.");
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
RpcJsonObjectOps::~RpcJsonObjectOps()
|
|
{
|
|
}
|
|
|
|
Value RpcJsonObjectOps::InvokeMethod(RpcObjectReference ref, vint methodId, Ptr<IValueArray> arguments)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonObjectOps::InvokeMethod(RpcObjectReference, vint, Ptr<IValueArray>)#"
|
|
CHECK_ERROR(dispatcher, ERROR_MESSAGE_PREFIX L"Dispatcher is required.");
|
|
auto request = CreateRpcMessage(WString::Unmanaged(L"Request:IObjectOps_InvokeMethod"), dispatcher->AllocateRequestId(), sourceClientId);
|
|
AddJsonObjectField(request, WString::Unmanaged(L"targetClientId"), CreateJsonNumber(targetClientId == RpcClientId_Invalid ? ref.clientId : targetClientId));
|
|
AddJsonObjectField(request, WString::Unmanaged(L"ref"), CreateRpcObjectReferenceJson(ref));
|
|
AddJsonObjectField(request, WString::Unmanaged(L"methodId"), CreateJsonNumber(methodId));
|
|
AddJsonObjectField(request, WString::Unmanaged(L"arguments"), ValueArrayToJsonArray(arguments));
|
|
|
|
auto response = GetJsonObject(dispatcher->OnJsonRequest(request, IRpcJsonMessageDispatcher::RequestType::Direct));
|
|
CHECK_ERROR(GetJsonString(GetJsonObjectField(response, WString::Unmanaged(L"rpcMethod"))) == WString::Unmanaged(L"Response:IObjectOps_InvokeMethod"), ERROR_MESSAGE_PREFIX L"Unexpected response method.");
|
|
CHECK_ERROR(ReadRequestId(response) == ReadRequestId(request), ERROR_MESSAGE_PREFIX L"Unexpected response request id.");
|
|
return JsonResponseToMethodResult(GetJsonObjectField(response, WString::Unmanaged(L"response")));
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RpcJsonObjectOps::EndInvokeMethod(vint slot)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonObjectOps::EndInvokeMethod(vint)#"
|
|
CHECK_ERROR(dispatcher, ERROR_MESSAGE_PREFIX L"Dispatcher is required.");
|
|
auto request = CreateRpcMessage(WString::Unmanaged(L"Request:IObjectOps_EndInvokeMethod"), dispatcher->AllocateRequestId(), sourceClientId);
|
|
AddJsonObjectField(request, WString::Unmanaged(L"targetClientId"), CreateJsonNumber(targetClientId));
|
|
AddJsonObjectField(request, WString::Unmanaged(L"slot"), CreateJsonNumber(slot));
|
|
|
|
auto response = GetJsonObject(dispatcher->OnJsonRequest(request, IRpcJsonMessageDispatcher::RequestType::Direct));
|
|
CHECK_ERROR(GetJsonString(GetJsonObjectField(response, WString::Unmanaged(L"rpcMethod"))) == WString::Unmanaged(L"Response:IObjectOps_EndInvokeMethod"), ERROR_MESSAGE_PREFIX L"Unexpected response method.");
|
|
CHECK_ERROR(ReadRequestId(response) == ReadRequestId(request), ERROR_MESSAGE_PREFIX L"Unexpected response request id.");
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RpcJsonObjectOps::ObjectHold(RpcObjectReference ref, vint remoteClientId, bool hold)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonObjectOps::ObjectHold(RpcObjectReference, vint, bool)#"
|
|
CHECK_ERROR(dispatcher, ERROR_MESSAGE_PREFIX L"Dispatcher is required.");
|
|
auto request = CreateRpcMessage(WString::Unmanaged(L"Request:IObjectOps_ObjectHold"), dispatcher->AllocateRequestId(), sourceClientId == RpcClientId_Invalid ? remoteClientId : sourceClientId);
|
|
AddJsonObjectField(request, WString::Unmanaged(L"targetClientId"), CreateJsonNumber(targetClientId == RpcClientId_Invalid ? ref.clientId : targetClientId));
|
|
AddJsonObjectField(request, WString::Unmanaged(L"ref"), CreateRpcObjectReferenceJson(ref));
|
|
AddJsonObjectField(request, WString::Unmanaged(L"remoteClientId"), CreateJsonNumber(remoteClientId));
|
|
AddJsonObjectField(request, WString::Unmanaged(L"hold"), CreateJsonBool(hold));
|
|
|
|
auto response = GetJsonObject(dispatcher->OnJsonRequest(request, IRpcJsonMessageDispatcher::RequestType::Direct));
|
|
CHECK_ERROR(GetJsonString(GetJsonObjectField(response, WString::Unmanaged(L"rpcMethod"))) == WString::Unmanaged(L"Response:IObjectOps_ObjectHold"), ERROR_MESSAGE_PREFIX L"Unexpected response method.");
|
|
CHECK_ERROR(ReadRequestId(response) == ReadRequestId(request), ERROR_MESSAGE_PREFIX L"Unexpected response request id.");
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
Ptr<JsonNode> RpcJsonObjectOps::Translate(Ptr<JsonNode> message, IRpcObjectOps* ops, IRpcLifecycle* lifecycle)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonObjectOps::Translate(Ptr<JsonNode>, IRpcObjectOps*)#"
|
|
(void)lifecycle;
|
|
CHECK_ERROR(ops, ERROR_MESSAGE_PREFIX L"Object ops is required.");
|
|
auto request = GetJsonObject(message);
|
|
auto rpcMethod = GetJsonString(GetJsonObjectField(request, WString::Unmanaged(L"rpcMethod")));
|
|
auto requestId = ReadRequestId(request);
|
|
auto sourceClientId = ReadSourceClientId(request);
|
|
auto targetClientId = ReadTargetClientId(request);
|
|
auto responseMethod =
|
|
rpcMethod == WString::Unmanaged(L"Request:IObjectOps_InvokeMethod") ? WString::Unmanaged(L"Response:IObjectOps_InvokeMethod") :
|
|
rpcMethod == WString::Unmanaged(L"Request:IObjectOps_EndInvokeMethod") ? WString::Unmanaged(L"Response:IObjectOps_EndInvokeMethod") :
|
|
rpcMethod == WString::Unmanaged(L"Request:IObjectOps_ObjectHold") ? WString::Unmanaged(L"Response:IObjectOps_ObjectHold") :
|
|
WString::Empty;
|
|
CHECK_ERROR(responseMethod != WString::Empty, ERROR_MESSAGE_PREFIX L"Unexpected RPC method.");
|
|
auto response = CreateRpcMessage(responseMethod, requestId, lifecycle ? lifecycle->GetClientId() : targetClientId);
|
|
AddJsonObjectField(response, WString::Unmanaged(L"targetClientId"), CreateJsonNumber(sourceClientId));
|
|
|
|
if (rpcMethod == WString::Unmanaged(L"Request:IObjectOps_InvokeMethod"))
|
|
{
|
|
auto result = ops->InvokeMethod(
|
|
GetRpcObjectReferenceFromJson(GetJsonObjectField(request, WString::Unmanaged(L"ref"))),
|
|
GetJsonInt(GetJsonObjectField(request, WString::Unmanaged(L"methodId"))),
|
|
JsonArrayToValueArray(GetJsonObjectField(request, WString::Unmanaged(L"arguments")))
|
|
);
|
|
AddJsonObjectField(response, WString::Unmanaged(L"response"), MethodResultToJsonResponse(result));
|
|
}
|
|
else if (rpcMethod == WString::Unmanaged(L"Request:IObjectOps_EndInvokeMethod"))
|
|
{
|
|
ops->EndInvokeMethod(GetJsonInt(GetJsonObjectField(request, WString::Unmanaged(L"slot"))));
|
|
}
|
|
else if (rpcMethod == WString::Unmanaged(L"Request:IObjectOps_ObjectHold"))
|
|
{
|
|
ops->ObjectHold(
|
|
GetRpcObjectReferenceFromJson(GetJsonObjectField(request, WString::Unmanaged(L"ref"))),
|
|
GetJsonInt(GetJsonObjectField(request, WString::Unmanaged(L"remoteClientId"))),
|
|
GetJsonBool(GetJsonObjectField(request, WString::Unmanaged(L"hold")))
|
|
);
|
|
}
|
|
else
|
|
{
|
|
CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Unexpected RPC method.");
|
|
}
|
|
return response;
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcJsonObjectEventOps
|
|
***********************************************************************/
|
|
|
|
RpcJsonObjectEventOps::RpcJsonObjectEventOps(IRpcJsonMessageDispatcher* _dispatcher)
|
|
: dispatcher(_dispatcher)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonObjectEventOps::RpcJsonObjectEventOps(...)#"
|
|
CHECK_ERROR(dispatcher, ERROR_MESSAGE_PREFIX L"Dispatcher is required.");
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
RpcJsonObjectEventOps::RpcJsonObjectEventOps(vint _sourceClientId, IRpcJsonMessageDispatcher* _dispatcher)
|
|
: sourceClientId(_sourceClientId)
|
|
, dispatcher(_dispatcher)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonObjectEventOps::RpcJsonObjectEventOps(...)#"
|
|
CHECK_ERROR(dispatcher, ERROR_MESSAGE_PREFIX L"Dispatcher is required.");
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
RpcJsonObjectEventOps::~RpcJsonObjectEventOps()
|
|
{
|
|
}
|
|
|
|
Value RpcJsonObjectEventOps::InvokeEvent(RpcObjectReference ref, vint eventId, Ptr<IValueArray> arguments)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonObjectEventOps::InvokeEvent(RpcObjectReference, vint, Ptr<IValueArray>)#"
|
|
CHECK_ERROR(dispatcher, ERROR_MESSAGE_PREFIX L"Dispatcher is required.");
|
|
auto request = CreateRpcMessage(WString::Unmanaged(L"Request:IObjectEventOps_InvokeEvent"), dispatcher->AllocateRequestId(), sourceClientId);
|
|
AddJsonObjectField(request, WString::Unmanaged(L"ref"), CreateRpcObjectReferenceJson(ref));
|
|
AddJsonObjectField(request, WString::Unmanaged(L"eventId"), CreateJsonNumber(eventId));
|
|
AddJsonObjectField(request, WString::Unmanaged(L"arguments"), ValueArrayToJsonArray(arguments));
|
|
|
|
auto response = GetJsonObject(dispatcher->OnJsonRequest(request, IRpcJsonMessageDispatcher::RequestType::Broadcast));
|
|
CHECK_ERROR(GetJsonString(GetJsonObjectField(response, WString::Unmanaged(L"rpcMethod"))) == WString::Unmanaged(L"Response:Broadcast_Response"), ERROR_MESSAGE_PREFIX L"Unexpected response method.");
|
|
CHECK_ERROR(ReadRequestId(response) == ReadRequestId(request), ERROR_MESSAGE_PREFIX L"Unexpected response request id.");
|
|
return BoxValue(CreateSerializedEventExceptionMap(GetJsonObjectField(response, WString::Unmanaged(L"response"))));
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
Ptr<JsonNode> RpcJsonObjectEventOps::Translate(Ptr<JsonNode> message, IRpcObjectEventOps* ops, IRpcLifecycle* lifecycle)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonObjectEventOps::Translate(Ptr<JsonNode>, IRpcObjectEventOps*)#"
|
|
CHECK_ERROR(ops, ERROR_MESSAGE_PREFIX L"Object event ops is required.");
|
|
auto request = GetJsonObject(message);
|
|
auto rpcMethod = GetJsonString(GetJsonObjectField(request, WString::Unmanaged(L"rpcMethod")));
|
|
CHECK_ERROR(rpcMethod == WString::Unmanaged(L"Request:IObjectEventOps_InvokeEvent"), ERROR_MESSAGE_PREFIX L"Unexpected RPC method.");
|
|
auto requestId = ReadRequestId(request);
|
|
auto sourceClientId = ReadSourceClientId(request);
|
|
auto response = CreateRpcMessage(WString::Unmanaged(L"Response:Broadcast_Response"), requestId, lifecycle ? lifecycle->GetClientId() : RpcClientId_Invalid);
|
|
AddJsonObjectField(response, WString::Unmanaged(L"targetClientId"), CreateJsonNumber(sourceClientId));
|
|
|
|
auto result = ops->InvokeEvent(
|
|
GetRpcObjectReferenceFromJson(GetJsonObjectField(request, WString::Unmanaged(L"ref"))),
|
|
GetJsonInt(GetJsonObjectField(request, WString::Unmanaged(L"eventId"))),
|
|
JsonArrayToValueArray(GetJsonObjectField(request, WString::Unmanaged(L"arguments")))
|
|
);
|
|
AddJsonObjectField(response, WString::Unmanaged(L"response"), CreateEventExceptionResponse(ValueToJsonNode(result)));
|
|
return response;
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcJsonDispatcher
|
|
***********************************************************************/
|
|
|
|
RpcJsonDispatcher::RpcJsonDispatcher(vint _sourceClientId, IRpcJsonMessageDispatcher* _dispatcher)
|
|
: sourceClientId(_sourceClientId)
|
|
, dispatcher(_dispatcher)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonDispatcher::RpcJsonDispatcher(vint, IRpcJsonMessageDispatcher*)#"
|
|
CHECK_ERROR(sourceClientId != RpcClientId_Invalid, ERROR_MESSAGE_PREFIX L"Source client id is required.");
|
|
CHECK_ERROR(dispatcher, ERROR_MESSAGE_PREFIX L"Dispatcher is required.");
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RpcJsonDispatcher::Finalize()
|
|
{
|
|
}
|
|
|
|
void RpcJsonDispatcher::Initialize()
|
|
{
|
|
}
|
|
|
|
void RpcJsonDispatcher::DeclareLocalService(RpcObjectReference ref)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonDispatcher::DeclareLocalService(RpcObjectReference)#"
|
|
CHECK_ERROR(dispatcher, ERROR_MESSAGE_PREFIX L"Dispatcher is required.");
|
|
CHECK_ERROR(ref.clientId == sourceClientId, ERROR_MESSAGE_PREFIX L"Client id does not match this dispatcher.");
|
|
auto request = CreateRpcMessage(WString::Unmanaged(L"Request:IRpcDispatcher_DeclareRemoteService"), dispatcher->AllocateRequestId(), sourceClientId);
|
|
AddJsonObjectField(request, WString::Unmanaged(L"ref"), CreateRpcObjectReferenceJson(ref));
|
|
|
|
auto response = dispatcher->OnJsonRequest(request, IRpcJsonMessageDispatcher::RequestType::BroadcastAndDrop);
|
|
CHECK_ERROR(!response, ERROR_MESSAGE_PREFIX L"DeclareLocalService should not receive a response.");
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
IRpcObjectEventOps* RpcJsonDispatcher::BroadcastFromClient_ObjectEventOps(vint selfClientId)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonDispatcher::BroadcastFromClient_ObjectEventOps(vint)#"
|
|
CHECK_ERROR(selfClientId == sourceClientId, ERROR_MESSAGE_PREFIX L"Client id does not match this dispatcher.");
|
|
if (!objectEventOps)
|
|
{
|
|
objectEventOps = Ptr(new RpcJsonObjectEventOps(sourceClientId, dispatcher));
|
|
}
|
|
return objectEventOps.Obj();
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
IRpcObjectOps* RpcJsonDispatcher::SendToClient_ObjectOps(vint targetClientId)
|
|
{
|
|
if (auto index = objectOps.Keys().IndexOf(targetClientId); index != -1)
|
|
{
|
|
return objectOps.Values()[index].Obj();
|
|
}
|
|
|
|
auto ops = Ptr(new RpcJsonObjectOps(sourceClientId, targetClientId, dispatcher));
|
|
objectOps.Set(targetClientId, ops);
|
|
return ops.Obj();
|
|
}
|
|
|
|
Ptr<JsonNode> RpcJsonDispatcher::Translate(Ptr<JsonNode> message, IRpcDispatcher* dispatcher, IRpcLifecycle* lifecycle)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonDispatcher::Translate(Ptr<JsonNode>, IRpcDispatcher*, IRpcLifecycle*)#"
|
|
(void)dispatcher;
|
|
CHECK_ERROR(lifecycle, ERROR_MESSAGE_PREFIX L"Lifecycle is required.");
|
|
auto request = GetJsonObject(message);
|
|
auto rpcMethod = GetJsonString(GetJsonObjectField(request, WString::Unmanaged(L"rpcMethod")));
|
|
CHECK_ERROR(rpcMethod == WString::Unmanaged(L"Request:IRpcDispatcher_DeclareRemoteService"), ERROR_MESSAGE_PREFIX L"Unexpected RPC method.");
|
|
auto sourceClientId = ReadSourceClientId(request);
|
|
auto ref = GetRpcObjectReferenceFromJson(GetJsonObjectField(request, WString::Unmanaged(L"ref")));
|
|
CHECK_ERROR(ref.clientId == sourceClientId, ERROR_MESSAGE_PREFIX L"Client id does not match the message source.");
|
|
|
|
lifecycle->DeclareRemoteService(ref);
|
|
return nullptr;
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RpcJsonLifecycle
|
|
***********************************************************************/
|
|
|
|
RpcJsonLifecycle::RpcJsonLifecycle(vint _clientId, RpcJsonDispatcher* _dispatcher)
|
|
: RpcLifecycleBase(_clientId)
|
|
, dispatcher(_dispatcher)
|
|
{
|
|
#define ERROR_MESSAGE_PREFIX L"vl::rpc_controller::RpcJsonLifecycle::RpcJsonLifecycle(vint, RpcJsonDispatcher*)#"
|
|
CHECK_ERROR(dispatcher, ERROR_MESSAGE_PREFIX L"Dispatcher is required.");
|
|
#undef ERROR_MESSAGE_PREFIX
|
|
}
|
|
|
|
void RpcJsonLifecycle::Register(
|
|
Ptr<IRpcSerializer> _serializer,
|
|
Ptr<IRpcObjectOps> _objectOps,
|
|
Ptr<IRpcObjectEventOps> _objectEventOps,
|
|
Func<vint(IDescriptable*)> _getTypeId,
|
|
Func<void(RpcObjectReference, IDescriptable*)> _eventAttacher
|
|
)
|
|
{
|
|
serializer = _serializer;
|
|
getTypeId = _getTypeId;
|
|
eventAttacher = _eventAttacher;
|
|
listOps = Ptr(new RpcCalleeListOps(this, serializer.Obj()));
|
|
listEventOps = Ptr(new RpcCalleeListEventOps(this, serializer.Obj()));
|
|
objectOpsForList = Ptr(new RpcCalleeObjectOpsForList(listOps, _objectOps, serializer.Obj()));
|
|
objectEventOpsForList = Ptr(new RpcCalleeObjectEventOpsForList(listEventOps, _objectEventOps, serializer.Obj()));
|
|
GetController()->Register(objectOpsForList, objectEventOpsForList);
|
|
}
|
|
|
|
vint RpcJsonLifecycle::DecideTypeId(IDescriptable* obj)const
|
|
{
|
|
auto result = RpcLifecycleBase::DecideTypeId(obj);
|
|
if (result != RpcTypeId_NotFound) return result;
|
|
return getTypeId ? getTypeId(obj) : RpcTypeId_NotFound;
|
|
}
|
|
|
|
IRpcSerializer* RpcJsonLifecycle::GetSerializer()
|
|
{
|
|
return serializer.Obj();
|
|
}
|
|
|
|
IRpcDispatcher* RpcJsonLifecycle::GetDispatcher()
|
|
{
|
|
return dispatcher;
|
|
}
|
|
|
|
void RpcJsonLifecycle::AttachLocalObjectEvents(RpcObjectReference ref, IDescriptable* obj)
|
|
{
|
|
if (eventAttacher)
|
|
{
|
|
eventAttacher(ref, obj);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* Request Id
|
|
***********************************************************************/
|
|
|
|
vint ReadRequestId(Ptr<JsonNode> message)
|
|
{
|
|
return GetJsonInt(GetJsonObjectField(GetJsonObject(message), WString::Unmanaged(L"rpcRequestId")));
|
|
}
|
|
|
|
void WriteRequestId(Ptr<JsonNode> message, vint requestId)
|
|
{
|
|
SetJsonObjectField(GetJsonObject(message), WString::Unmanaged(L"rpcRequestId"), CreateJsonNumber(requestId));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
.\RPCJSON\WFLIBRARYRPCJSONDISPATCHERCLIENT.CPP
|
|
***********************************************************************/
|
|
|
|
#include <cwchar>
|
|
|
|
namespace vl::rpc_controller::channeling
|
|
{
|
|
using namespace vl;
|
|
using namespace vl::collections;
|
|
using namespace vl::glr::json;
|
|
using namespace vl::inter_process;
|
|
using namespace vl::reflection;
|
|
using namespace vl::reflection::description;
|
|
using namespace vl::rpc_controller;
|
|
|
|
namespace
|
|
{
|
|
Ptr<JsonString> CreateJsonString(const WString& value)
|
|
{
|
|
auto node = Ptr(new JsonString);
|
|
node->content.value = value;
|
|
return node;
|
|
}
|
|
|
|
Ptr<JsonNumber> CreateJsonNumber(vint value)
|
|
{
|
|
auto node = Ptr(new JsonNumber);
|
|
node->content.value = itow(value);
|
|
return node;
|
|
}
|
|
|
|
Ptr<JsonObject> CreateJsonObject()
|
|
{
|
|
return Ptr(new JsonObject);
|
|
}
|
|
|
|
Ptr<JsonObject> GetJsonObject(Ptr<JsonNode> node)
|
|
{
|
|
auto object = node.Cast<JsonObject>();
|
|
CHECK_ERROR(object, L"RpcJsonDispatcherClient expects a JSON object.");
|
|
return object;
|
|
}
|
|
|
|
void AddJsonObjectField(Ptr<JsonObject> object, const WString& name, Ptr<JsonNode> value)
|
|
{
|
|
auto field = Ptr(new JsonObjectField);
|
|
field->name.value = name;
|
|
field->value = value;
|
|
object->fields.Add(field);
|
|
}
|
|
|
|
void SetJsonObjectField(Ptr<JsonObject> object, const WString& name, Ptr<JsonNode> value)
|
|
{
|
|
for (auto field : object->fields)
|
|
{
|
|
if (field->name.value == name)
|
|
{
|
|
field->value = value;
|
|
return;
|
|
}
|
|
}
|
|
AddJsonObjectField(object, name, value);
|
|
}
|
|
|
|
Ptr<JsonNode> FindJsonObjectField(Ptr<JsonObject> object, const WString& name)
|
|
{
|
|
for (auto field : object->fields)
|
|
{
|
|
if (field->name.value == name)
|
|
{
|
|
return field->value;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Ptr<JsonNode> GetJsonObjectField(Ptr<JsonObject> object, const WString& name)
|
|
{
|
|
auto field = FindJsonObjectField(object, name);
|
|
CHECK_ERROR(field, L"RpcJsonDispatcherClient expects a JSON object field.");
|
|
return field;
|
|
}
|
|
|
|
WString GetJsonString(Ptr<JsonNode> node)
|
|
{
|
|
auto stringNode = node.Cast<JsonString>();
|
|
CHECK_ERROR(stringNode, L"RpcJsonDispatcherClient expects a JSON string.");
|
|
return stringNode->content.value;
|
|
}
|
|
|
|
vint GetJsonInt(Ptr<JsonNode> node)
|
|
{
|
|
auto numberNode = node.Cast<JsonNumber>();
|
|
CHECK_ERROR(numberNode, L"RpcJsonDispatcherClient expects a JSON number.");
|
|
return wtoi(numberNode->content.value);
|
|
}
|
|
|
|
bool StartsWith(const WString& value, const wchar_t* prefix)
|
|
{
|
|
auto prefixLength = (vint)wcslen(prefix);
|
|
return value.Length() >= prefixLength && wcsncmp(value.Buffer(), prefix, prefixLength) == 0;
|
|
}
|
|
|
|
WString TryReadRpcMethod(Ptr<JsonNode> message)
|
|
{
|
|
if (auto object = message.Cast<JsonObject>())
|
|
{
|
|
if (auto field = FindJsonObjectField(object, WString::Unmanaged(L"rpcMethod")))
|
|
{
|
|
if (auto stringNode = field.Cast<JsonString>())
|
|
{
|
|
return stringNode->content.value;
|
|
}
|
|
}
|
|
}
|
|
return WString::Empty;
|
|
}
|
|
|
|
bool IsRpcRequest(const WString& rpcMethod)
|
|
{
|
|
return StartsWith(rpcMethod, L"Request:");
|
|
}
|
|
|
|
bool IsRpcResponse(const WString& rpcMethod)
|
|
{
|
|
return StartsWith(rpcMethod, L"Response:");
|
|
}
|
|
|
|
bool IsDeclareRemoteServiceRequest(const WString& rpcMethod)
|
|
{
|
|
return rpcMethod == WString::Unmanaged(L"Request:IRpcDispatcher_DeclareRemoteService");
|
|
}
|
|
|
|
IRpcJsonMessageDispatcher::RequestType GetRequestType(const WString& rpcMethod)
|
|
{
|
|
if (StartsWith(rpcMethod, L"Request:IObjectOps_"))
|
|
{
|
|
return IRpcJsonMessageDispatcher::RequestType::Direct;
|
|
}
|
|
if (StartsWith(rpcMethod, L"Request:IObjectEventOps_"))
|
|
{
|
|
return IRpcJsonMessageDispatcher::RequestType::Broadcast;
|
|
}
|
|
if (IsDeclareRemoteServiceRequest(rpcMethod))
|
|
{
|
|
return IRpcJsonMessageDispatcher::RequestType::BroadcastAndDrop;
|
|
}
|
|
CHECK_FAIL(L"RpcJsonDispatcherClient received an unknown request method.");
|
|
return IRpcJsonMessageDispatcher::RequestType::Direct;
|
|
}
|
|
|
|
vint ReadRequestId(Ptr<JsonNode> message)
|
|
{
|
|
return GetJsonInt(GetJsonObjectField(GetJsonObject(message), WString::Unmanaged(L"rpcRequestId")));
|
|
}
|
|
|
|
vint ReadTargetClientId(Ptr<JsonNode> message)
|
|
{
|
|
return GetJsonInt(GetJsonObjectField(GetJsonObject(message), WString::Unmanaged(L"targetClientId")));
|
|
}
|
|
|
|
vint ReadDeclaredServiceTypeId(Ptr<JsonNode> message)
|
|
{
|
|
auto ref = GetJsonObject(GetJsonObjectField(GetJsonObject(message), WString::Unmanaged(L"ref")));
|
|
return GetJsonInt(GetJsonObjectField(ref, WString::Unmanaged(L"typeId")));
|
|
}
|
|
|
|
Ptr<JsonObject> CreateLoginMessage(vint serverClientId)
|
|
{
|
|
auto message = CreateJsonObject();
|
|
AddJsonObjectField(message, WString::Unmanaged(L"rpcChannelingSystem"), CreateJsonString(WString::Unmanaged(L"Login")));
|
|
AddJsonObjectField(message, WString::Unmanaged(L"serverClientId"), CreateJsonNumber(serverClientId));
|
|
return message;
|
|
}
|
|
|
|
Ptr<JsonObject> CreateLogoutMessage()
|
|
{
|
|
auto message = CreateJsonObject();
|
|
AddJsonObjectField(message, WString::Unmanaged(L"rpcChannelingSystem"), CreateJsonString(WString::Unmanaged(L"Logout")));
|
|
return message;
|
|
}
|
|
|
|
bool TryReadLoginMessage(Ptr<JsonNode> message, vint& serverClientId)
|
|
{
|
|
if (auto object = message.Cast<JsonObject>())
|
|
{
|
|
auto systemField = FindJsonObjectField(object, WString::Unmanaged(L"rpcChannelingSystem"));
|
|
if (systemField && GetJsonString(systemField) == WString::Unmanaged(L"Login"))
|
|
{
|
|
serverClientId = GetJsonInt(GetJsonObjectField(object, WString::Unmanaged(L"serverClientId")));
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
RpcJsonDispatcherClient
|
|
***********************************************************************/
|
|
|
|
RpcJsonDispatcherClient::RpcJsonDispatcherClient()
|
|
{
|
|
CHECK_ERROR(semaphoreMessages.Create(0, 65536), L"RpcJsonDispatcherClient failed to create the message semaphore.");
|
|
CHECK_ERROR(eventServerLocalClientId.CreateManualUnsignal(false), L"RpcJsonDispatcherClient failed to create the server-client-id event.");
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::PrepareConnection(JsonChannel* channel, const List<WString>& _waitingForServices)
|
|
{
|
|
CHECK_ERROR(channel, L"RpcJsonDispatcherClient needs an RPC channel.");
|
|
CHECK_ERROR(!rpcChannel, L"RpcJsonDispatcherClient connection can only be initialized once.");
|
|
rpcChannel = channel;
|
|
CopyFrom(waitingForServices, _waitingForServices);
|
|
if (waitingForServices.Count() > 0)
|
|
{
|
|
eventWaitingForServicesCreated = true;
|
|
CHECK_ERROR(eventWaitingForServices.CreateManualUnsignal(false), L"RpcJsonDispatcherClient failed to create the service-waiting event.");
|
|
}
|
|
rpcChannel->Initialize(this);
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::SetRpcObjects(Ptr<RpcJsonDispatcher> _rpcDispatcher, Ptr<RpcJsonLifecycle> _lifecycle)
|
|
{
|
|
CHECK_ERROR(_rpcDispatcher, L"RpcJsonDispatcherClient needs a dispatcher.");
|
|
CHECK_ERROR(_lifecycle, L"RpcJsonDispatcherClient needs a lifecycle.");
|
|
CHECK_ERROR(!rpcDispatcher, L"RpcJsonDispatcherClient RPC objects can only be initialized once.");
|
|
rpcDispatcher = _rpcDispatcher;
|
|
lifecycle = _lifecycle;
|
|
}
|
|
|
|
RpcJsonLifecycle* RpcJsonDispatcherClient::GetRpcJsonLifecycle()
|
|
{
|
|
CHECK_ERROR(lifecycle, L"RpcJsonDispatcherClient needs a lifecycle.");
|
|
return lifecycle.Obj();
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::ProcessCachedIncomingServiceDeclarations()
|
|
{
|
|
List<JsonPackage> cached;
|
|
SPIN_LOCK(lockServiceDeclarations)
|
|
{
|
|
for (auto request : cachedIncomingServiceDeclarations)
|
|
{
|
|
cached.Add(request);
|
|
}
|
|
cachedIncomingServiceDeclarations.Clear();
|
|
}
|
|
|
|
for (auto request : cached)
|
|
{
|
|
ProcessIncomingServiceDeclaration(request);
|
|
}
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::SendCachedOutgoingServiceDeclarations()
|
|
{
|
|
List<JsonPackage> cached;
|
|
SPIN_LOCK(lockServiceDeclarations)
|
|
{
|
|
for (auto request : cachedOutgoingServiceDeclarations)
|
|
{
|
|
cached.Add(request);
|
|
}
|
|
cachedOutgoingServiceDeclarations.Clear();
|
|
}
|
|
|
|
for (auto request : cached)
|
|
{
|
|
SendJsonRequest(request, RequestType::BroadcastAndDrop);
|
|
}
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::ProcessIncomingServiceDeclaration(JsonPackage request)
|
|
{
|
|
IRpcJsonMessageDispatcher::DefaultTranslate(
|
|
request,
|
|
RequestType::BroadcastAndDrop,
|
|
lifecycle->GetController()->GetObjectOps(),
|
|
lifecycle->GetController()->GetObjectEventOps(),
|
|
lifecycle->GetDispatcher(),
|
|
lifecycle.Obj()
|
|
);
|
|
UpdateWaitingForServices(request);
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::UpdateWaitingForServices(JsonPackage request)
|
|
{
|
|
auto typeId = ReadDeclaredServiceTypeId(request);
|
|
bool shouldSignal = false;
|
|
SPIN_LOCK(lockWaitingForServices)
|
|
{
|
|
for (vint i = waitingForServices.Count(); i > 0; i--)
|
|
{
|
|
auto index = i - 1;
|
|
if (lifecycle->GetTypeIdFromName(waitingForServices[index]) == typeId)
|
|
{
|
|
waitingForServices.RemoveAt(index);
|
|
}
|
|
}
|
|
shouldSignal = waitingForServices.Count() == 0;
|
|
}
|
|
if (shouldSignal && eventWaitingForServicesCreated)
|
|
{
|
|
eventWaitingForServices.Signal();
|
|
}
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::WaitForServerClientId()
|
|
{
|
|
if (serverLocalClientId.load() == -1)
|
|
{
|
|
eventServerLocalClientId.Wait();
|
|
}
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::WaitForExpectedServices()
|
|
{
|
|
if (eventWaitingForServicesCreated)
|
|
{
|
|
eventWaitingForServices.Wait();
|
|
}
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::SendJsonRequest(JsonPackage message, RequestType requestType)
|
|
{
|
|
CHECK_ERROR(rpcChannel, L"RpcJsonDispatcherClient needs an RPC channel.");
|
|
switch (requestType)
|
|
{
|
|
case RequestType::Direct:
|
|
rpcChannel->SendToClient(ReadTargetClientId(message), message);
|
|
break;
|
|
case RequestType::Broadcast:
|
|
case RequestType::BroadcastAndDrop:
|
|
WaitForServerClientId();
|
|
rpcChannel->SendToClient(serverLocalClientId.load(), message);
|
|
break;
|
|
default:
|
|
CHECK_FAIL(L"RpcJsonDispatcherClient received an unknown request type.");
|
|
}
|
|
FlushChannel();
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::PushReceivedMessage(vint senderClientId, JsonPackage message)
|
|
{
|
|
SPIN_LOCK(lockMessages)
|
|
{
|
|
messages.Add({ senderClientId, message });
|
|
}
|
|
semaphoreMessages.Release();
|
|
}
|
|
|
|
RpcJsonDispatcherClient::ReceivedJsonMessage RpcJsonDispatcherClient::PopReceivedMessage()
|
|
{
|
|
semaphoreMessages.Wait();
|
|
ReceivedJsonMessage result;
|
|
SPIN_LOCK(lockMessages)
|
|
{
|
|
CHECK_ERROR(messages.Count() > 0, L"RpcJsonDispatcherClient message queue is unexpectedly empty.");
|
|
result = messages[0];
|
|
messages.RemoveAt(0);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool RpcJsonDispatcherClient::TryPopBufferedResponse(vint requestId, ReceivedJsonMessage& message)
|
|
{
|
|
SPIN_LOCK(lockMessages)
|
|
{
|
|
for (vint i = 0; i < bufferedResponses.Count(); i++)
|
|
{
|
|
auto item = bufferedResponses[i];
|
|
if (ReadRequestId(item.message) == requestId)
|
|
{
|
|
message = item;
|
|
bufferedResponses.RemoveAt(i);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::PushBufferedResponse(ReceivedJsonMessage message)
|
|
{
|
|
SPIN_LOCK(lockMessages)
|
|
{
|
|
bufferedResponses.Add(message);
|
|
}
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::SendJsonResponse(vint receiverClientId, JsonPackage response)
|
|
{
|
|
if (response)
|
|
{
|
|
CHECK_ERROR(rpcChannel, L"RpcJsonDispatcherClient needs an RPC channel.");
|
|
rpcChannel->SendToClient(receiverClientId, response);
|
|
FlushChannel();
|
|
}
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::FlushChannel()
|
|
{
|
|
CHECK_ERROR(rpcChannel, L"RpcJsonDispatcherClient needs an RPC channel.");
|
|
bool disconnected = false;
|
|
rpcChannel->BatchWrite(disconnected);
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::ProcessRequestAndSendResponse(vint senderClientId, JsonPackage request)
|
|
{
|
|
auto response = TranslateRequest(request);
|
|
SendJsonResponse(senderClientId, response);
|
|
}
|
|
|
|
JsonPackage RpcJsonDispatcherClient::TranslateRequest(JsonPackage request)
|
|
{
|
|
auto rpcMethod = TryReadRpcMethod(request);
|
|
CHECK_ERROR(IsRpcRequest(rpcMethod), L"RpcJsonDispatcherClient expects an RPC request.");
|
|
|
|
return IRpcJsonMessageDispatcher::DefaultTranslate(
|
|
request,
|
|
GetRequestType(rpcMethod),
|
|
lifecycle->GetController()->GetObjectOps(),
|
|
lifecycle->GetController()->GetObjectEventOps(),
|
|
lifecycle->GetDispatcher(),
|
|
lifecycle.Obj()
|
|
);
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::WaitForServer(JsonChannelClient* channelClient, JsonChannel* channel, const List<WString>& _waitingForServices)
|
|
{
|
|
CHECK_ERROR(channelClient, L"RpcJsonDispatcherClient needs a channel client.");
|
|
PrepareConnection(channel, _waitingForServices);
|
|
channelClient->WaitForServer();
|
|
CHECK_ERROR(channelClient->GetStatus() == ClientStatus::Connected, L"RpcJsonDispatcherClient failed to connect to the server.");
|
|
}
|
|
|
|
vint RpcJsonDispatcherClient::ConnectLocalServer(JsonChannelServer* channelServer, Ptr<JsonChannelClient> localClient, JsonChannel* channel, const List<WString>& _waitingForServices)
|
|
{
|
|
CHECK_ERROR(channelServer, L"RpcJsonDispatcherClient needs a channel server.");
|
|
CHECK_ERROR(localClient, L"RpcJsonDispatcherClient needs a local client.");
|
|
PrepareConnection(channel, _waitingForServices);
|
|
auto clientId = channelServer->ConnectLocalClient(localClient);
|
|
CHECK_ERROR(clientId != -1, L"RpcJsonDispatcherClient failed to connect the local client.");
|
|
return clientId;
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::Initialize()
|
|
{
|
|
CHECK_ERROR(lifecycle, L"RpcJsonDispatcherClient needs to connect before Initialize.");
|
|
CHECK_ERROR(initialized.load() == 0, L"RpcJsonDispatcherClient can only be initialized once.");
|
|
ProcessCachedIncomingServiceDeclarations();
|
|
lifecycle->Initialize();
|
|
initialized.store(1);
|
|
SendCachedOutgoingServiceDeclarations();
|
|
WaitForExpectedServices();
|
|
}
|
|
|
|
IRpcLifecycle* RpcJsonDispatcherClient::GetRpcLifecycle()
|
|
{
|
|
CHECK_ERROR(lifecycle, L"RpcJsonDispatcherClient needs a lifecycle.");
|
|
return lifecycle.Obj();
|
|
}
|
|
|
|
IRpcDispatcher* RpcJsonDispatcherClient::GetRpcDispatcher()
|
|
{
|
|
CHECK_ERROR(rpcDispatcher, L"RpcJsonDispatcherClient needs a dispatcher.");
|
|
return rpcDispatcher.Obj();
|
|
}
|
|
|
|
vint RpcJsonDispatcherClient::AllocateRequestId()
|
|
{
|
|
return ++nextRequestId;
|
|
}
|
|
|
|
JsonPackage RpcJsonDispatcherClient::OnJsonRequest(JsonPackage message, RequestType requestType)
|
|
{
|
|
auto rpcMethod = TryReadRpcMethod(message);
|
|
CHECK_ERROR(rpcMethod != WString::Empty, L"RpcJsonDispatcherClient expects an RPC message.");
|
|
|
|
if (requestType == RequestType::BroadcastAndDrop)
|
|
{
|
|
CHECK_ERROR(IsDeclareRemoteServiceRequest(rpcMethod), L"RpcJsonDispatcherClient expects a service declaration.");
|
|
if (initialized.load() == 0)
|
|
{
|
|
SPIN_LOCK(lockServiceDeclarations)
|
|
{
|
|
cachedOutgoingServiceDeclarations.Add(message);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SendJsonRequest(message, requestType);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
CHECK_ERROR(initialized.load() != 0, L"RpcJsonDispatcherClient only accepts service declarations before Initialize.");
|
|
|
|
struct ActiveJsonRequest
|
|
{
|
|
atomic_vint& counter;
|
|
|
|
ActiveJsonRequest(atomic_vint& _counter)
|
|
: counter(_counter)
|
|
{
|
|
counter++;
|
|
}
|
|
|
|
~ActiveJsonRequest()
|
|
{
|
|
counter--;
|
|
}
|
|
};
|
|
|
|
ActiveJsonRequest active(activeJsonRequests);
|
|
auto requestId = ReadRequestId(message);
|
|
SendJsonRequest(message, requestType);
|
|
|
|
while (true)
|
|
{
|
|
ReceivedJsonMessage item;
|
|
if (!TryPopBufferedResponse(requestId, item))
|
|
{
|
|
item = PopReceivedMessage();
|
|
}
|
|
rpcMethod = TryReadRpcMethod(item.message);
|
|
CHECK_ERROR(rpcMethod != WString::Empty, L"RpcJsonDispatcherClient expects an RPC message.");
|
|
|
|
if (IsRpcRequest(rpcMethod))
|
|
{
|
|
ProcessRequestAndSendResponse(item.senderClientId, item.message);
|
|
continue;
|
|
}
|
|
|
|
CHECK_ERROR(IsRpcResponse(rpcMethod), L"RpcJsonDispatcherClient expects an RPC request or response.");
|
|
if (ReadRequestId(item.message) == requestId)
|
|
{
|
|
return item.message;
|
|
}
|
|
|
|
PushBufferedResponse(item);
|
|
}
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::OnRead(vint senderClientId, const JsonPackage& package)
|
|
{
|
|
vint newServerLocalClientId = -1;
|
|
if (TryReadLoginMessage(package, newServerLocalClientId))
|
|
{
|
|
serverLocalClientId.store(newServerLocalClientId);
|
|
eventServerLocalClientId.Signal();
|
|
return;
|
|
}
|
|
|
|
auto rpcMethod = TryReadRpcMethod(package);
|
|
CHECK_ERROR(rpcMethod != WString::Empty, L"RpcJsonDispatcherClient expects an RPC message.");
|
|
|
|
if (IsDeclareRemoteServiceRequest(rpcMethod))
|
|
{
|
|
if (initialized.load() == 0)
|
|
{
|
|
SPIN_LOCK(lockServiceDeclarations)
|
|
{
|
|
cachedIncomingServiceDeclarations.Add(package);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ProcessIncomingServiceDeclaration(package);
|
|
}
|
|
return;
|
|
}
|
|
|
|
CHECK_ERROR(initialized.load() != 0, L"RpcJsonDispatcherClient only accepts service declarations before Initialize.");
|
|
|
|
if (activeJsonRequests.load() > 0)
|
|
{
|
|
PushReceivedMessage(senderClientId, package);
|
|
return;
|
|
}
|
|
|
|
if (IsRpcRequest(rpcMethod))
|
|
{
|
|
ScheduleTask(Func<void()>([this, senderClientId, package]()
|
|
{
|
|
ProcessRequestAndSendResponse(senderClientId, package);
|
|
}));
|
|
}
|
|
else
|
|
{
|
|
CHECK_ERROR(IsRpcResponse(rpcMethod), L"RpcJsonDispatcherClient expects an RPC request or response.");
|
|
CHECK_FAIL(L"RpcJsonDispatcherClient received an RPC response while no request is waiting.");
|
|
}
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::FinalizeRpc()
|
|
{
|
|
if (lifecycle)
|
|
{
|
|
lifecycle->Finalize();
|
|
lifecycle = nullptr;
|
|
rpcDispatcher = nullptr;
|
|
}
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::SetServerLocalClientId(vint clientId)
|
|
{
|
|
CHECK_ERROR(clientId != -1, L"RpcJsonDispatcherClient needs a valid server client id.");
|
|
serverLocalClientId.store(clientId);
|
|
eventServerLocalClientId.Signal();
|
|
}
|
|
|
|
void RpcJsonDispatcherClient::NotifyServerClientDisconnected()
|
|
{
|
|
CHECK_ERROR(rpcChannel, L"RpcJsonDispatcherClient needs an RPC channel.");
|
|
WaitForServerClientId();
|
|
rpcChannel->SendToClient(serverLocalClientId.load(), CreateLogoutMessage());
|
|
FlushChannel();
|
|
}
|
|
|
|
/***********************************************************************
|
|
RpcJsonDispatcherClientForTaskQueue
|
|
***********************************************************************/
|
|
|
|
void RpcJsonDispatcherClientForTaskQueue::ScheduleTask(Func<void()> task)
|
|
{
|
|
taskQueue->QueueTask(task);
|
|
}
|
|
|
|
RpcJsonDispatcherClientForTaskQueue::RpcJsonDispatcherClientForTaskQueue(Ptr<TaskQueue> _taskQueue)
|
|
: taskQueue(_taskQueue)
|
|
{
|
|
CHECK_ERROR(taskQueue, L"RpcJsonDispatcherClientForTaskQueue needs a task queue.");
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
.\RPCJSON\WFLIBRARYRPCJSONDISPATCHERSERVER.CPP
|
|
***********************************************************************/
|
|
|
|
|
|
namespace vl::rpc_controller::channeling
|
|
{
|
|
using namespace vl;
|
|
using namespace vl::collections;
|
|
using namespace vl::glr::json;
|
|
|
|
namespace
|
|
{
|
|
Ptr<JsonLiteral> ServerCreateJsonLiteral(JsonLiteralValue value)
|
|
{
|
|
auto node = Ptr(new JsonLiteral);
|
|
node->value = value;
|
|
return node;
|
|
}
|
|
|
|
Ptr<JsonString> ServerCreateJsonString(const WString& value)
|
|
{
|
|
auto node = Ptr(new JsonString);
|
|
node->content.value = value;
|
|
return node;
|
|
}
|
|
|
|
Ptr<JsonNumber> ServerCreateJsonNumber(vint value)
|
|
{
|
|
auto node = Ptr(new JsonNumber);
|
|
node->content.value = itow(value);
|
|
return node;
|
|
}
|
|
|
|
Ptr<JsonArray> ServerCreateJsonArray()
|
|
{
|
|
return Ptr(new JsonArray);
|
|
}
|
|
|
|
Ptr<JsonObject> ServerCreateJsonObject()
|
|
{
|
|
return Ptr(new JsonObject);
|
|
}
|
|
|
|
Ptr<JsonObject> ServerGetJsonObject(Ptr<JsonNode> node)
|
|
{
|
|
auto object = node.Cast<JsonObject>();
|
|
CHECK_ERROR(object, L"RpcJsonDispatcherServer expects a JSON object.");
|
|
return object;
|
|
}
|
|
|
|
Ptr<JsonArray> ServerGetJsonArray(Ptr<JsonNode> node)
|
|
{
|
|
auto array = node.Cast<JsonArray>();
|
|
CHECK_ERROR(array, L"RpcJsonDispatcherServer expects a JSON array.");
|
|
return array;
|
|
}
|
|
|
|
void ServerAddJsonObjectField(Ptr<JsonObject> object, const WString& name, Ptr<JsonNode> value)
|
|
{
|
|
auto field = Ptr(new JsonObjectField);
|
|
field->name.value = name;
|
|
field->value = value;
|
|
object->fields.Add(field);
|
|
}
|
|
|
|
void ServerSetJsonObjectField(Ptr<JsonObject> object, const WString& name, Ptr<JsonNode> value)
|
|
{
|
|
for (auto field : object->fields)
|
|
{
|
|
if (field->name.value == name)
|
|
{
|
|
field->value = value;
|
|
return;
|
|
}
|
|
}
|
|
ServerAddJsonObjectField(object, name, value);
|
|
}
|
|
|
|
Ptr<JsonNode> ServerFindJsonObjectField(Ptr<JsonObject> object, const WString& name)
|
|
{
|
|
for (auto field : object->fields)
|
|
{
|
|
if (field->name.value == name)
|
|
{
|
|
return field->value;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Ptr<JsonNode> ServerGetJsonObjectField(Ptr<JsonObject> object, const WString& name)
|
|
{
|
|
auto field = ServerFindJsonObjectField(object, name);
|
|
CHECK_ERROR(field, L"RpcJsonDispatcherServer expects a JSON object field.");
|
|
return field;
|
|
}
|
|
|
|
WString ServerGetJsonString(Ptr<JsonNode> node)
|
|
{
|
|
auto stringNode = node.Cast<JsonString>();
|
|
CHECK_ERROR(stringNode, L"RpcJsonDispatcherServer expects a JSON string.");
|
|
return stringNode->content.value;
|
|
}
|
|
|
|
vint ServerGetJsonInt(Ptr<JsonNode> node)
|
|
{
|
|
auto numberNode = node.Cast<JsonNumber>();
|
|
CHECK_ERROR(numberNode, L"RpcJsonDispatcherServer expects a JSON number.");
|
|
return wtoi(numberNode->content.value);
|
|
}
|
|
|
|
bool ServerIsJsonNull(Ptr<JsonNode> node)
|
|
{
|
|
auto literal = node.Cast<JsonLiteral>();
|
|
return literal && literal->value == JsonLiteralValue::Null;
|
|
}
|
|
|
|
bool ServerStartsWith(const WString& value, const wchar_t* prefix)
|
|
{
|
|
auto prefixLength = (vint)wcslen(prefix);
|
|
return value.Length() >= prefixLength && wcsncmp(value.Buffer(), prefix, prefixLength) == 0;
|
|
}
|
|
|
|
WString ServerTryReadRpcMethod(Ptr<JsonNode> message)
|
|
{
|
|
if (auto object = message.Cast<JsonObject>())
|
|
{
|
|
if (auto field = ServerFindJsonObjectField(object, WString::Unmanaged(L"rpcMethod")))
|
|
{
|
|
if (auto stringNode = field.Cast<JsonString>())
|
|
{
|
|
return stringNode->content.value;
|
|
}
|
|
}
|
|
}
|
|
return WString::Empty;
|
|
}
|
|
|
|
bool ServerIsRpcRequest(const WString& rpcMethod)
|
|
{
|
|
return ServerStartsWith(rpcMethod, L"Request:");
|
|
}
|
|
|
|
bool ServerIsRpcResponse(const WString& rpcMethod)
|
|
{
|
|
return ServerStartsWith(rpcMethod, L"Response:");
|
|
}
|
|
|
|
bool ServerIsBroadcastRequest(const WString& rpcMethod)
|
|
{
|
|
return rpcMethod == WString::Unmanaged(L"Request:IObjectEventOps_InvokeEvent");
|
|
}
|
|
|
|
bool ServerIsBroadcastAndDropRequest(const WString& rpcMethod)
|
|
{
|
|
return rpcMethod == WString::Unmanaged(L"Request:IRpcDispatcher_DeclareRemoteService");
|
|
}
|
|
|
|
vint ServerReadRequestId(Ptr<JsonNode> message)
|
|
{
|
|
return ServerGetJsonInt(ServerGetJsonObjectField(ServerGetJsonObject(message), WString::Unmanaged(L"rpcRequestId")));
|
|
}
|
|
|
|
void ServerWriteRequestId(Ptr<JsonNode> message, vint requestId)
|
|
{
|
|
ServerSetJsonObjectField(ServerGetJsonObject(message), WString::Unmanaged(L"rpcRequestId"), ServerCreateJsonNumber(requestId));
|
|
}
|
|
|
|
vint ServerReadSourceClientId(Ptr<JsonNode> message)
|
|
{
|
|
return ServerGetJsonInt(ServerGetJsonObjectField(ServerGetJsonObject(message), WString::Unmanaged(L"sourceClientId")));
|
|
}
|
|
|
|
void ServerWriteSourceClientId(Ptr<JsonNode> message, vint clientId)
|
|
{
|
|
ServerSetJsonObjectField(ServerGetJsonObject(message), WString::Unmanaged(L"sourceClientId"), ServerCreateJsonNumber(clientId));
|
|
}
|
|
|
|
Ptr<JsonObject> ServerCreateRpcMessage(const WString& rpcMethod, vint requestId, vint sourceClientId)
|
|
{
|
|
auto message = ServerCreateJsonObject();
|
|
ServerAddJsonObjectField(message, WString::Unmanaged(L"rpcMethod"), ServerCreateJsonString(rpcMethod));
|
|
ServerAddJsonObjectField(message, WString::Unmanaged(L"rpcRequestId"), ServerCreateJsonNumber(requestId));
|
|
ServerAddJsonObjectField(message, WString::Unmanaged(L"sourceClientId"), ServerCreateJsonNumber(sourceClientId));
|
|
return message;
|
|
}
|
|
|
|
Ptr<JsonObject> ServerCreateLoginMessage(vint serverClientId)
|
|
{
|
|
auto message = ServerCreateJsonObject();
|
|
ServerAddJsonObjectField(message, WString::Unmanaged(L"rpcChannelingSystem"), ServerCreateJsonString(WString::Unmanaged(L"Login")));
|
|
ServerAddJsonObjectField(message, WString::Unmanaged(L"serverClientId"), ServerCreateJsonNumber(serverClientId));
|
|
return message;
|
|
}
|
|
|
|
bool ServerTryReadLogoutMessage(Ptr<JsonNode> message)
|
|
{
|
|
if (auto object = message.Cast<JsonObject>())
|
|
{
|
|
auto systemField = ServerFindJsonObjectField(object, WString::Unmanaged(L"rpcChannelingSystem"));
|
|
return systemField && ServerGetJsonString(systemField) == WString::Unmanaged(L"Logout");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ServerIsBroadcastReady(Ptr<RpcJsonDispatcherServer::PendingBroadcast> pending)
|
|
{
|
|
for (auto clientId : pending->expectedClientIds)
|
|
{
|
|
if (!pending->responses.Keys().Contains(clientId))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
RpcJsonDispatcherServer
|
|
***********************************************************************/
|
|
|
|
RpcJsonDispatcherServer::RpcJsonDispatcherServer(JsonChannelClient* _serverClient, JsonChannel* channel)
|
|
{
|
|
CHECK_ERROR(_serverClient, L"RpcJsonDispatcherServer needs a server channel client.");
|
|
CHECK_ERROR(channel, L"RpcJsonDispatcherServer needs an RPC channel.");
|
|
serverClient = _serverClient;
|
|
rpcChannel = channel;
|
|
rpcChannel->Initialize(this);
|
|
}
|
|
|
|
vint RpcJsonDispatcherServer::AllocateRequestId()
|
|
{
|
|
return ++nextRequestId;
|
|
}
|
|
|
|
WString RpcJsonDispatcherServer::MakeBroadcastKey(vint clientId, vint requestId)
|
|
{
|
|
return itow(clientId) + WString::Unmanaged(L":") + itow(requestId);
|
|
}
|
|
|
|
JsonPackage RpcJsonDispatcherServer::CreateBroadcastResponse(vint sourceClientId, vint targetClientId, vint requestId, Ptr<PendingBroadcast> pending)
|
|
{
|
|
auto response = ServerCreateRpcMessage(WString::Unmanaged(L"Response:Broadcast_Response"), requestId, sourceClientId);
|
|
ServerAddJsonObjectField(response, WString::Unmanaged(L"targetClientId"), ServerCreateJsonNumber(targetClientId));
|
|
|
|
if (pending && pending->hasNonNullResponse)
|
|
{
|
|
auto consolidated = ServerCreateJsonArray();
|
|
for (auto clientId : pending->expectedClientIds)
|
|
{
|
|
auto index = pending->responses.Keys().IndexOf(clientId);
|
|
if (index != -1)
|
|
{
|
|
auto responseObject = ServerGetJsonObject(pending->responses.Values()[index]);
|
|
auto responseField = ServerFindJsonObjectField(responseObject, WString::Unmanaged(L"response"));
|
|
if (responseField && !ServerIsJsonNull(responseField))
|
|
{
|
|
auto array = ServerGetJsonArray(responseField);
|
|
for (auto item : array->items)
|
|
{
|
|
consolidated->items.Add(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ServerAddJsonObjectField(response, WString::Unmanaged(L"response"), consolidated);
|
|
}
|
|
else
|
|
{
|
|
ServerAddJsonObjectField(response, WString::Unmanaged(L"response"), ServerCreateJsonLiteral(JsonLiteralValue::Null));
|
|
}
|
|
return response;
|
|
}
|
|
|
|
RpcJsonDispatcherServer::CompletedBroadcast RpcJsonDispatcherServer::CompleteBroadcastLocked(const WString& key)
|
|
{
|
|
auto pending = pendingBroadcasts[key];
|
|
auto serverClientId = GetServerClientId();
|
|
CompletedBroadcast completed;
|
|
completed.originalClientId = pending->originalClientId;
|
|
completed.response = CreateBroadcastResponse(serverClientId, pending->originalClientId, pending->originalRequestId, pending);
|
|
|
|
redirectedBroadcasts.Remove(pending->redirectedRequestId);
|
|
pendingBroadcasts.Remove(key);
|
|
return completed;
|
|
}
|
|
|
|
void RpcJsonDispatcherServer::DeliverCompletedBroadcast(const CompletedBroadcast& completed)
|
|
{
|
|
if (completed.response)
|
|
{
|
|
SendJsonResponse(completed.originalClientId, completed.response);
|
|
}
|
|
}
|
|
|
|
JsonPackage RpcJsonDispatcherServer::StartBroadcast(vint originalClientId, vint originalRequestId, JsonPackage message)
|
|
{
|
|
auto serverClientId = GetServerClientId();
|
|
auto redirectedRequestId = AllocateRequestId();
|
|
|
|
copy_visitor::AstVisitor copier;
|
|
auto redirectedMessage = copier.CopyNode(message.Obj());
|
|
ServerWriteRequestId(redirectedMessage, redirectedRequestId);
|
|
ServerWriteSourceClientId(redirectedMessage, serverClientId);
|
|
|
|
JsonPackage immediateResponse;
|
|
bool shouldFlush = false;
|
|
List<vint> blockedReceivers;
|
|
auto pending = Ptr(new PendingBroadcast);
|
|
pending->originalClientId = originalClientId;
|
|
pending->originalRequestId = originalRequestId;
|
|
pending->redirectedRequestId = redirectedRequestId;
|
|
|
|
SPIN_LOCK(lockBroadcasts)
|
|
{
|
|
for (auto clientId : connectedClientIds)
|
|
{
|
|
if (clientId != originalClientId)
|
|
{
|
|
pending->expectedClientIds.Add(clientId);
|
|
}
|
|
}
|
|
|
|
if (pending->expectedClientIds.Count() == 0)
|
|
{
|
|
immediateResponse = CreateBroadcastResponse(serverClientId, originalClientId, originalRequestId, nullptr);
|
|
}
|
|
else
|
|
{
|
|
auto key = MakeBroadcastKey(originalClientId, originalRequestId);
|
|
pendingBroadcasts.Add(key, pending);
|
|
redirectedBroadcasts.Add(redirectedRequestId, key);
|
|
|
|
if (connectedClientIds.Contains(originalClientId))
|
|
{
|
|
blockedReceivers.Add(originalClientId);
|
|
}
|
|
CHECK_ERROR(rpcChannel, L"RpcJsonDispatcherServer needs an RPC channel.");
|
|
rpcChannel->BroadcastFromClient(redirectedMessage, blockedReceivers);
|
|
shouldFlush = true;
|
|
}
|
|
}
|
|
|
|
if (shouldFlush)
|
|
{
|
|
FlushChannel();
|
|
}
|
|
return immediateResponse;
|
|
}
|
|
|
|
void RpcJsonDispatcherServer::StartBroadcastAndDrop(vint originalClientId, JsonPackage message)
|
|
{
|
|
List<vint> blockedReceivers;
|
|
SPIN_LOCK(lockBroadcasts)
|
|
{
|
|
if (connectedClientIds.Contains(originalClientId))
|
|
{
|
|
blockedReceivers.Add(originalClientId);
|
|
}
|
|
CHECK_ERROR(rpcChannel, L"RpcJsonDispatcherServer needs an RPC channel.");
|
|
rpcChannel->BroadcastFromClient(message, blockedReceivers);
|
|
}
|
|
FlushChannel();
|
|
}
|
|
|
|
bool RpcJsonDispatcherServer::TryHandleBroadcastResponse(vint senderClientId, JsonPackage response)
|
|
{
|
|
if (ServerTryReadRpcMethod(response) != WString::Unmanaged(L"Response:Broadcast_Response"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CompletedBroadcast completed;
|
|
auto redirectedRequestId = ServerReadRequestId(response);
|
|
bool handled = false;
|
|
|
|
SPIN_LOCK(lockBroadcasts)
|
|
{
|
|
auto index = redirectedBroadcasts.Keys().IndexOf(redirectedRequestId);
|
|
if (index != -1)
|
|
{
|
|
handled = true;
|
|
auto key = redirectedBroadcasts.Values()[index];
|
|
auto pending = pendingBroadcasts[key];
|
|
|
|
if (pending->expectedClientIds.Contains(senderClientId))
|
|
{
|
|
auto responseObject = ServerGetJsonObject(response);
|
|
auto responseField = ServerFindJsonObjectField(responseObject, WString::Unmanaged(L"response"));
|
|
if (responseField && !ServerIsJsonNull(responseField))
|
|
{
|
|
pending->hasNonNullResponse = true;
|
|
}
|
|
pending->responses.Set(senderClientId, response);
|
|
|
|
if (ServerIsBroadcastReady(pending))
|
|
{
|
|
completed = CompleteBroadcastLocked(key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DeliverCompletedBroadcast(completed);
|
|
return handled;
|
|
}
|
|
|
|
void RpcJsonDispatcherServer::HandleServiceDeclaration(vint senderClientId, JsonPackage request)
|
|
{
|
|
SPIN_LOCK(lockBroadcasts)
|
|
{
|
|
cachedServiceDeclarations.Add(request);
|
|
}
|
|
StartBroadcastAndDrop(senderClientId, request);
|
|
}
|
|
|
|
void RpcJsonDispatcherServer::SendLoginMessages(vint clientId)
|
|
{
|
|
List<JsonPackage> declarations;
|
|
SPIN_LOCK(lockBroadcasts)
|
|
{
|
|
for (auto request : cachedServiceDeclarations)
|
|
{
|
|
declarations.Add(request);
|
|
}
|
|
}
|
|
|
|
CHECK_ERROR(rpcChannel, L"RpcJsonDispatcherServer needs an RPC channel.");
|
|
rpcChannel->SendToClient(clientId, ServerCreateLoginMessage(GetServerClientId()));
|
|
for (auto request : declarations)
|
|
{
|
|
rpcChannel->SendToClient(clientId, request);
|
|
}
|
|
FlushChannel();
|
|
}
|
|
|
|
void RpcJsonDispatcherServer::SendJsonResponse(vint receiverClientId, JsonPackage response)
|
|
{
|
|
CHECK_ERROR(rpcChannel, L"RpcJsonDispatcherServer needs an RPC channel.");
|
|
rpcChannel->SendToClient(receiverClientId, response);
|
|
FlushChannel();
|
|
}
|
|
|
|
void RpcJsonDispatcherServer::FlushChannel()
|
|
{
|
|
CHECK_ERROR(rpcChannel, L"RpcJsonDispatcherServer needs an RPC channel.");
|
|
bool disconnected = false;
|
|
rpcChannel->BatchWrite(disconnected);
|
|
}
|
|
|
|
bool RpcJsonDispatcherServer::HasServerClientId()
|
|
{
|
|
return serverClient->GetClientId() != -1;
|
|
}
|
|
|
|
void RpcJsonDispatcherServer::RegisterClient(vint clientId)
|
|
{
|
|
CHECK_ERROR(clientId != -1, L"RpcJsonDispatcherServer needs a valid client id.");
|
|
CHECK_ERROR(HasServerClientId(), L"RpcJsonDispatcherServer needs its server local client to connect first.");
|
|
|
|
SPIN_LOCK(lockBroadcasts)
|
|
{
|
|
if (!connectedClientIds.Contains(clientId))
|
|
{
|
|
connectedClientIds.Add(clientId);
|
|
}
|
|
}
|
|
|
|
ScheduleTask(Func<void()>([this, clientId]()
|
|
{
|
|
SendLoginMessages(clientId);
|
|
}));
|
|
}
|
|
|
|
void RpcJsonDispatcherServer::DisconnectClient(vint clientId)
|
|
{
|
|
if (clientId == GetServerClientId())
|
|
{
|
|
return;
|
|
}
|
|
|
|
List<CompletedBroadcast> completedBroadcasts;
|
|
SPIN_LOCK(lockBroadcasts)
|
|
{
|
|
if (connectedClientIds.Contains(clientId))
|
|
{
|
|
connectedClientIds.Remove(clientId);
|
|
}
|
|
|
|
for (vint i = pendingBroadcasts.Count(); i > 0; i--)
|
|
{
|
|
auto index = i - 1;
|
|
auto key = pendingBroadcasts.Keys().Get(index);
|
|
auto pending = pendingBroadcasts.Values().Get(index);
|
|
auto expectedIndex = pending->expectedClientIds.IndexOf(clientId);
|
|
if (expectedIndex != -1)
|
|
{
|
|
pending->expectedClientIds.RemoveAt(expectedIndex);
|
|
if (ServerIsBroadcastReady(pending))
|
|
{
|
|
completedBroadcasts.Add(CompleteBroadcastLocked(key));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto&& completed : completedBroadcasts)
|
|
{
|
|
DeliverCompletedBroadcast(completed);
|
|
}
|
|
}
|
|
|
|
vint RpcJsonDispatcherServer::GetServerClientId()
|
|
{
|
|
auto result = serverClient->GetClientId();
|
|
CHECK_ERROR(result != -1, L"RpcJsonDispatcherServer server client has not been connected.");
|
|
return result;
|
|
}
|
|
|
|
void RpcJsonDispatcherServer::OnRead(vint senderClientId, const JsonPackage& package)
|
|
{
|
|
if (ServerTryReadLogoutMessage(package))
|
|
{
|
|
DisconnectClient(senderClientId);
|
|
return;
|
|
}
|
|
|
|
auto rpcMethod = ServerTryReadRpcMethod(package);
|
|
CHECK_ERROR(rpcMethod != WString::Empty, L"RpcJsonDispatcherServer expects an RPC message.");
|
|
|
|
if (ServerIsRpcRequest(rpcMethod))
|
|
{
|
|
auto originalClientId = ServerReadSourceClientId(package);
|
|
CHECK_ERROR(originalClientId == senderClientId, L"RpcJsonDispatcherServer received a request with a mismatched source client id.");
|
|
|
|
if (ServerIsBroadcastAndDropRequest(rpcMethod))
|
|
{
|
|
HandleServiceDeclaration(originalClientId, package);
|
|
}
|
|
else if (ServerIsBroadcastRequest(rpcMethod))
|
|
{
|
|
auto immediateResponse = StartBroadcast(originalClientId, ServerReadRequestId(package), package);
|
|
if (immediateResponse)
|
|
{
|
|
SendJsonResponse(originalClientId, immediateResponse);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CHECK_FAIL(L"RpcJsonDispatcherServer only accepts broadcast requests.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
CHECK_ERROR(ServerIsRpcResponse(rpcMethod), L"RpcJsonDispatcherServer expects an RPC request or response.");
|
|
if (!TryHandleBroadcastResponse(senderClientId, package))
|
|
{
|
|
CHECK_FAIL(L"RpcJsonDispatcherServer received an unmatched RPC response.");
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
RpcJsonDispatcherServerForTaskQueue
|
|
***********************************************************************/
|
|
|
|
void RpcJsonDispatcherServerForTaskQueue::ScheduleTask(Func<void()> task)
|
|
{
|
|
taskQueue->QueueTask(task);
|
|
}
|
|
|
|
RpcJsonDispatcherServerForTaskQueue::RpcJsonDispatcherServerForTaskQueue(JsonChannelClient* _serverClient, JsonChannel* channel, Ptr<TaskQueue> _taskQueue)
|
|
: RpcJsonDispatcherServer(_serverClient, channel)
|
|
, taskQueue(_taskQueue)
|
|
{
|
|
CHECK_ERROR(taskQueue, L"RpcJsonDispatcherServerForTaskQueue needs a task queue.");
|
|
}
|
|
}
|
|
|