Skip to content
Snippets Groups Projects
Commit 6b53036f authored by Nat Goodspeed's avatar Nat Goodspeed
Browse files

DRTVWR-564: Allow LLLeapListener to report LazyEventAPIs too.

One important factor in the design of LazyEventAPI was the desire to allow
LLLeapListener to query metadata for an LLEventAPI even if it hasn't yet been
instantiated by LazyEventAPI. That's why LazyEventAPI requires the same
metadata required by a classic LLEventAPI.

Instead of just publicly exposing its data members, give LazyEventAPI a query
API mimicking LLEventAPI / LLEventDispatcher. Protect data members and private
methods.

Adapt lazyeventapi_test.cpp accordingly.

Extend LLLeapListener::getAPIs() and getAPI() to look through LazyEventAPIBase
instances after first checking existing LLEventAPI instances. Because the
query API for LazyEventAPIBase mimics LLEventAPI's, extract getAPI()'s actual
metadata reporting to a new internal template function reportAPI().

While we're touching LLLeapListener, we no longer need BOOST_FOREACH().
parent f9d810ac
No related branches found
No related tags found
No related merge requests found
......@@ -15,9 +15,11 @@
#include "lazyeventapi.h"
// STL headers
// std headers
#include <algorithm> // std::find_if
// external library headers
// other Linden headers
#include "llevents.h"
#include "llsdutil.h"
LL::LazyEventAPIBase::LazyEventAPIBase(
const std::string& name, const std::string& desc, const std::string& field)
......@@ -51,3 +53,20 @@ LL::LazyEventAPIBase::~LazyEventAPIBase()
LLEventPumps::instance().unregisterPumpFactory(mParams.name);
}
}
LLSD LL::LazyEventAPIBase::getMetadata(const std::string& name) const
{
// Since mOperations is a vector rather than a map, just search.
auto found = std::find_if(mOperations.begin(), mOperations.end(),
[&name](const auto& namedesc)
{ return (namedesc.first == name); });
if (found == mOperations.end())
return {};
// LLEventDispatcher() supplements the returned metadata in different
// ways, depending on metadata provided to the specific add() method.
// Don't try to emulate all that. At some point we might consider more
// closely unifying LLEventDispatcher machinery with LazyEventAPI, but for
// now this will have to do.
return llsd::map("name", found->first, "desc", found->second);
}
......@@ -63,9 +63,6 @@ namespace LL
LazyEventAPIBase& operator=(const LazyEventAPIBase&) = delete;
LazyEventAPIBase& operator=(LazyEventAPIBase&&) = delete;
// actually instantiate the companion LLEventAPI subclass
virtual LLEventPump* construct(const std::string& name) = 0;
// capture add() calls we want to play back on LLEventAPI construction
template <typename... ARGS>
void add(const std::string& name, const std::string& desc, ARGS&&... rest)
......@@ -96,12 +93,40 @@ namespace LL
});
}
// The following queries mimic the LLEventAPI / LLEventDispatcher
// query API.
// Get the string name of the subject LLEventAPI
std::string getName() const { return mParams.name; }
// Get the documentation string
std::string getDesc() const { return mParams.desc; }
// Retrieve the LLSD key we use for dispatching
std::string getDispatchKey() const { return mParams.field; }
// operations
using NameDesc = std::pair<std::string, std::string>;
private:
// metadata that might be queried by LLLeapListener
std::vector<std::pair<std::string, std::string>> mOperations;
std::vector<NameDesc> mOperations;
public:
using const_iterator = decltype(mOperations)::const_iterator;
const_iterator begin() const { return mOperations.begin(); }
const_iterator end() const { return mOperations.end(); }
LLSD getMetadata(const std::string& name) const;
protected:
// Params with which to instantiate the companion LLEventAPI subclass
LazyEventAPIParams mParams;
private:
// true if we successfully registered our LLEventAPI on construction
bool mRegistered;
// actually instantiate the companion LLEventAPI subclass
virtual LLEventPump* construct(const std::string& name) = 0;
// Passing an overloaded function to any function that accepts an
// arbitrary callable is a PITB because you have to specify the
// correct overload. What we want is for the compiler to select the
......@@ -117,8 +142,6 @@ namespace LL
{
instance->add(std::forward<ARGS>(args)...);
}
bool mRegistered;
};
/**
......@@ -168,6 +191,7 @@ namespace LL
LazyEventAPIBase(name, desc, field)
{}
private:
LLEventPump* construct(const std::string& /*name*/) override
{
// base class has carefully assembled LazyEventAPIParams embedded
......
......@@ -14,14 +14,16 @@
// associated header
#include "llleaplistener.h"
// STL headers
#include <map>
#include <algorithm> // std::find_if
#include <functional>
#include <map>
#include <set>
// std headers
// external library headers
#include <boost/foreach.hpp>
// other Linden headers
#include "lluuid.h"
#include "lazyeventapi.h"
#include "llsdutil.h"
#include "lluuid.h"
#include "stringize.h"
/*****************************************************************************
......@@ -110,7 +112,7 @@ LLLeapListener::~LLLeapListener()
// value_type, and Bad Things would happen if you copied an
// LLTempBoundListener. (Destruction of the original would disconnect the
// listener, invalidating every stored connection.)
BOOST_FOREACH(ListenersMap::value_type& pair, mListeners)
for (ListenersMap::value_type& pair : mListeners)
{
pair.second.disconnect();
}
......@@ -208,31 +210,65 @@ void LLLeapListener::getAPIs(const LLSD& request) const
{
Response reply(LLSD(), request);
// first, traverse existing LLEventAPI instances
std::set<std::string> instances;
for (auto& ea : LLEventAPI::instance_snapshot())
{
LLSD info;
info["desc"] = ea.getDesc();
reply[ea.getName()] = info;
// remember which APIs are actually instantiated
instances.insert(ea.getName());
reply[ea.getName()] = llsd::map("desc", ea.getDesc());
}
// supplement that with *potential* instances: that is, instances of
// LazyEventAPI that can each instantiate an LLEventAPI on demand
for (const auto& lea : LL::LazyEventAPIBase::instance_snapshot())
{
// skip any LazyEventAPI that's already instantiated its LLEventAPI
if (instances.find(lea.getName()) == instances.end())
{
reply[lea.getName()] = llsd::map("desc", lea.getDesc());
}
}
}
// Because LazyEventAPI deliberately mimics LLEventAPI's query API, this
// function can be passed either -- even though they're unrelated types.
template <typename API>
void reportAPI(LLEventAPI::Response& reply, const API& api)
{
reply["name"] = api.getName();
reply["desc"] = api.getDesc();
reply["key"] = api.getDispatchKey();
LLSD ops;
for (const auto& namedesc : api)
{
ops.append(api.getMetadata(namedesc.first));
}
reply["ops"] = ops;
}
void LLLeapListener::getAPI(const LLSD& request) const
{
Response reply(LLSD(), request);
auto found = LLEventAPI::getInstance(request["api"]);
if (found)
// check first among existing LLEventAPI instances
auto foundea = LLEventAPI::getInstance(request["api"]);
if (foundea)
{
reportAPI(reply, *foundea);
}
else
{
reply["name"] = found->getName();
reply["desc"] = found->getDesc();
reply["key"] = found->getDispatchKey();
LLSD ops;
for (LLEventAPI::const_iterator oi(found->begin()), oend(found->end());
oi != oend; ++oi)
// Here the requested LLEventAPI doesn't yet exist, but do we have a
// registered LazyEventAPI for it?
LL::LazyEventAPIBase::instance_snapshot snap;
auto foundlea = std::find_if(snap.begin(), snap.end(),
[api = request["api"].asString()]
(const auto& lea)
{ return (lea.getName() == api); });
if (foundlea != snap.end())
{
ops.append(found->getMetadata(oi->first));
reportAPI(reply, *foundlea);
}
reply["ops"] = ops;
}
}
......
......@@ -109,16 +109,28 @@ namespace tut
{
set_test_name("LazyEventAPI metadata");
MyRegistrar regster;
// Of course we have 'regster' in hand; we don't need to search for
// it. But this next test verifies that we can find (all) LazyEventAPI
// instances using LazyEventAPIBase::instance_snapshot. Normally we
// wouldn't search; normally we'd just look at each instance in the
// loop body.
const MyRegistrar* found = nullptr;
for (const auto& registrar : LL::LazyEventAPIBase::instance_snapshot())
if ((found = dynamic_cast<const MyRegistrar*>(&registrar)))
break;
ensure("Failed to find MyRegistrar via LLInstanceTracker", found);
ensure_equals("wrong API name", found->mParams.name, "Test");
ensure_contains("wrong API desc", found->mParams.desc, "test LLEventAPI");
ensure_equals("wrong API field", found->mParams.field, "op");
ensure_equals("failed to find operations", found->mOperations.size(), 1);
ensure_equals("wrong operation name", found->mOperations[0].first, "set");
ensure_contains("wrong operation desc", found->mOperations[0].second, "set operation");
ensure_equals("wrong API name", found->getName(), "Test");
ensure_contains("wrong API desc", found->getDesc(), "test LLEventAPI");
ensure_equals("wrong API field", found->getDispatchKey(), "op");
// Normally we'd just iterate over *found. But for test purposes,
// actually capture the range of NameDesc pairs in a vector.
std::vector<LL::LazyEventAPIBase::NameDesc> ops{ found->begin(), found->end() };
ensure_equals("failed to find operations", ops.size(), 1);
ensure_equals("wrong operation name", ops[0].first, "set");
ensure_contains("wrong operation desc", ops[0].second, "set operation");
LLSD metadata{ found->getMetadata(ops[0].first) };
ensure_equals("bad metadata name", metadata["name"].asString(), ops[0].first);
ensure_equals("bad metadata desc", metadata["desc"].asString(), ops[0].second);
}
} // namespace tut
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment