mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-13 15:31:17 -06:00
* Make it possible to query a propertyowner by uri with the getpropertytopic * Remove automatic reloading of ui from drag and drop script * Add function for uri for propertyowners * Add uri to the to_json for the propertyowner * Add comment for the propertyOwner function and update the comment for the property function to clearer distinguish between uris and identifiers * Go back to the old events but with uris instead as their member * Apply feedback from PR * Address PR comments --------- Co-authored-by: Ylva Selling <ylva.selling@liu.se>
390 lines
13 KiB
C++
390 lines
13 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* OpenSpace *
|
|
* *
|
|
* Copyright (c) 2014-2024 *
|
|
* *
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
|
* software and associated documentation files (the "Software"), to deal in the Software *
|
|
* without restriction, including without limitation the rights to use, copy, modify, *
|
|
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
|
* permit persons to whom the Software is furnished to do so, subject to the following *
|
|
* conditions: *
|
|
* *
|
|
* The above copyright notice and this permission notice shall be included in all copies *
|
|
* or substantial portions of the Software. *
|
|
* *
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
|
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
|
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
|
****************************************************************************************/
|
|
|
|
#include <openspace/properties/propertyowner.h>
|
|
|
|
#include <openspace/engine/globals.h>
|
|
#include <openspace/events/event.h>
|
|
#include <openspace/events/eventengine.h>
|
|
#include <openspace/properties/property.h>
|
|
#include <openspace/scene/scene.h>
|
|
#include <ghoul/format.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <ghoul/misc/assert.h>
|
|
#include <ghoul/misc/invariants.h>
|
|
#include <algorithm>
|
|
#include <numeric>
|
|
|
|
namespace {
|
|
constexpr std::string_view _loggerCat = "PropertyOwner";
|
|
} // namespace
|
|
|
|
namespace openspace::properties {
|
|
|
|
PropertyOwner::PropertyOwner(PropertyOwnerInfo info)
|
|
: _identifier(std::move(info.identifier))
|
|
, _guiName(std::move(info.guiName))
|
|
, _description(std::move(info.description))
|
|
{
|
|
ghoul_precondition(
|
|
_identifier.find_first_of("\t\n ") == std::string::npos,
|
|
"Identifier must contain any whitespaces"
|
|
);
|
|
ghoul_precondition(
|
|
_identifier.find_first_of('.') == std::string::npos,
|
|
"Identifier must contain any dots"
|
|
);
|
|
}
|
|
|
|
PropertyOwner::~PropertyOwner() {
|
|
_properties.clear();
|
|
_subOwners.clear();
|
|
}
|
|
|
|
const std::vector<Property*>& PropertyOwner::properties() const {
|
|
return _properties;
|
|
}
|
|
|
|
std::vector<Property*> PropertyOwner::propertiesRecursive() const {
|
|
std::vector<Property*> props = properties();
|
|
|
|
for (const PropertyOwner* owner : _subOwners) {
|
|
std::vector<Property*> p = owner->propertiesRecursive();
|
|
props.insert(props.end(), p.begin(), p.end());
|
|
}
|
|
|
|
return props;
|
|
}
|
|
|
|
Property* PropertyOwner::property(const std::string& uri) const {
|
|
auto it = std::find_if(
|
|
_properties.begin(),
|
|
_properties.end(),
|
|
[&uri](Property* prop) { return prop->identifier() == uri; }
|
|
);
|
|
|
|
if (it == _properties.end() || (*it)->identifier() != uri) {
|
|
// if we do not own the searched property, it must consist of a concatenated
|
|
// name and we can delegate it to a subowner
|
|
const size_t ownerSeparator = uri.find(URISeparator);
|
|
if (ownerSeparator == std::string::npos) {
|
|
// if we do not own the property and there is no separator, it does not exist
|
|
return nullptr;
|
|
}
|
|
else {
|
|
const std::string ownerName = uri.substr(0, ownerSeparator);
|
|
const std::string propertyName = uri.substr(ownerSeparator + 1);
|
|
|
|
PropertyOwner* owner = propertySubOwner(ownerName);
|
|
if (!owner) {
|
|
return nullptr;
|
|
}
|
|
else {
|
|
// Recurse into the subOwner
|
|
return owner->property(propertyName);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
return *it;
|
|
}
|
|
}
|
|
|
|
PropertyOwner* PropertyOwner::propertyOwner(const std::string& uri) const {
|
|
PropertyOwner* directChild = propertySubOwner(uri);
|
|
if (directChild) {
|
|
return directChild;
|
|
}
|
|
|
|
// If we do not own the searched PropertyOwner, it must consist of a concatenated
|
|
// name and we can delegate it to a subowner
|
|
const size_t ownerSeparator = uri.find(URISeparator);
|
|
if (ownerSeparator == std::string::npos) {
|
|
// if we do not own the PropertyOwner and there is no separator, it does not exist
|
|
return nullptr;
|
|
}
|
|
else {
|
|
const std::string parentName = uri.substr(0, ownerSeparator);
|
|
const std::string ownerName = uri.substr(ownerSeparator + 1);
|
|
|
|
PropertyOwner* owner = propertySubOwner(parentName);
|
|
return owner ? owner->propertyOwner(ownerName) : nullptr;
|
|
}
|
|
}
|
|
|
|
std::string PropertyOwner::uri() const {
|
|
std::string identifier = _identifier;
|
|
PropertyOwner* currentOwner = owner();
|
|
while (currentOwner) {
|
|
const std::string& ownerId = currentOwner->identifier();
|
|
if (!ownerId.empty()) {
|
|
identifier = std::format("{}.{}", ownerId, identifier);
|
|
}
|
|
currentOwner = currentOwner->owner();
|
|
}
|
|
return identifier;
|
|
}
|
|
|
|
bool PropertyOwner::hasProperty(const std::string& uri) const {
|
|
return property(uri) != nullptr;
|
|
}
|
|
|
|
bool PropertyOwner::hasProperty(const Property* prop) const {
|
|
ghoul_precondition(prop != nullptr, "prop must not be nullptr");
|
|
|
|
std::vector<Property*>::const_iterator it = std::find(
|
|
_properties.begin(), _properties.end(), prop
|
|
);
|
|
|
|
return it != _properties.end();
|
|
}
|
|
|
|
const std::vector<PropertyOwner*>& PropertyOwner::propertySubOwners() const {
|
|
return _subOwners;
|
|
}
|
|
|
|
PropertyOwner* PropertyOwner::propertySubOwner(const std::string& identifier) const {
|
|
std::vector<PropertyOwner*>::const_iterator it = std::find_if(
|
|
_subOwners.begin(),
|
|
_subOwners.end(),
|
|
[&identifier](PropertyOwner* owner) { return owner->identifier() == identifier; }
|
|
);
|
|
|
|
if (it == _subOwners.end() || (*it)->identifier() != identifier) {
|
|
return nullptr;
|
|
}
|
|
else {
|
|
return *it;
|
|
}
|
|
}
|
|
|
|
bool PropertyOwner::hasPropertySubOwner(const std::string& identifier) const {
|
|
return propertySubOwner(identifier) != nullptr;
|
|
}
|
|
|
|
void PropertyOwner::setPropertyGroupName(std::string groupID, std::string identifier) {
|
|
_groupNames[std::move(groupID)] = std::move(identifier);
|
|
}
|
|
|
|
std::string PropertyOwner::propertyGroupName(const std::string& groupID) const {
|
|
auto it = _groupNames.find(groupID);
|
|
if (it == _groupNames.end()) {
|
|
return groupID;
|
|
}
|
|
else {
|
|
return it->second;
|
|
}
|
|
}
|
|
|
|
void PropertyOwner::addProperty(Property* prop) {
|
|
ZoneScoped;
|
|
|
|
ghoul_precondition(prop != nullptr, "prop must not be nullptr");
|
|
|
|
if (prop->identifier().empty()) {
|
|
LERROR("No property identifier specified");
|
|
return;
|
|
}
|
|
// See if we can find the identifier of the property to add in the properties list
|
|
std::vector<Property*>::const_iterator it = std::find_if(
|
|
_properties.begin(),
|
|
_properties.end(),
|
|
[id = prop->identifier()](Property* p) { return p->identifier() == id; }
|
|
);
|
|
|
|
// If we found the property identifier, we need to bail out
|
|
if (it != _properties.end() && (*it)->identifier() == prop->identifier()) {
|
|
LERROR(std::format(
|
|
"Property identifier '{}' already present in PropertyOwner '{}'",
|
|
prop->identifier(),
|
|
identifier()
|
|
));
|
|
return;
|
|
}
|
|
else {
|
|
// Otherwise we still have to look if there is a PropertyOwner with the same name
|
|
const bool hasOwner = hasPropertySubOwner(prop->identifier());
|
|
if (hasOwner) {
|
|
LERROR(std::format(
|
|
"Property identifier '{}' already names a registered PropertyOwner",
|
|
prop->identifier()
|
|
));
|
|
return;
|
|
}
|
|
else {
|
|
_properties.push_back(prop);
|
|
prop->setPropertyOwner(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PropertyOwner::addProperty(Property& prop) {
|
|
addProperty(&prop);
|
|
}
|
|
|
|
void PropertyOwner::addPropertySubOwner(openspace::properties::PropertyOwner* owner) {
|
|
ZoneScoped;
|
|
|
|
ghoul_precondition(owner != nullptr, "owner must not be nullptr");
|
|
ghoul_precondition(
|
|
!owner->identifier().empty(),
|
|
"PropertyOwner must have an identifier"
|
|
);
|
|
|
|
// See if we can find the name of the propertyowner to add using the lower bound
|
|
std::vector<PropertyOwner*>::const_iterator it = std::find_if(
|
|
_subOwners.begin(),
|
|
_subOwners.end(),
|
|
[identifier = owner->identifier()](PropertyOwner* o) {
|
|
return o->identifier() == identifier;
|
|
}
|
|
);
|
|
|
|
// If we found the propertyowner's name, we need to bail out
|
|
if (it != _subOwners.end() && (*it)->identifier() == owner->identifier()) {
|
|
LERROR(std::format(
|
|
"PropertyOwner '{}' already present in PropertyOwner '{}'",
|
|
owner->identifier(),
|
|
identifier()
|
|
));
|
|
return;
|
|
}
|
|
else {
|
|
// We still need to check if the PropertyOwners name is used in a Property
|
|
const bool hasProp = hasProperty(owner->identifier());
|
|
if (hasProp) {
|
|
LERROR(std::format(
|
|
"PropertyOwner '{}'s name already names a Property", owner->identifier()
|
|
));
|
|
return;
|
|
}
|
|
else {
|
|
_subOwners.push_back(owner);
|
|
owner->setPropertyOwner(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PropertyOwner::addPropertySubOwner(openspace::properties::PropertyOwner& owner) {
|
|
addPropertySubOwner(&owner);
|
|
}
|
|
|
|
void PropertyOwner::removeProperty(Property* prop) {
|
|
ghoul_precondition(prop != nullptr, "prop must not be nullptr");
|
|
|
|
// See if we can find the identifier of the property to add in the properties list
|
|
std::vector<Property*>::const_iterator it = std::find_if(
|
|
_properties.begin(),
|
|
_properties.end(),
|
|
[id = prop->identifier()](Property* p) { return p->identifier() == id; }
|
|
);
|
|
|
|
// If we found the property identifier, we can delete it
|
|
if (it != _properties.end() && (*it)->identifier() == prop->identifier()) {
|
|
(*it)->setPropertyOwner(nullptr);
|
|
_properties.erase(it);
|
|
}
|
|
else {
|
|
LERROR(std::format(
|
|
"Property with identifier '{}' not found for removal", prop->identifier()
|
|
));
|
|
}
|
|
}
|
|
|
|
void PropertyOwner::removeProperty(Property& prop) {
|
|
removeProperty(&prop);
|
|
}
|
|
|
|
void PropertyOwner::removePropertySubOwner(openspace::properties::PropertyOwner* owner) {
|
|
ghoul_precondition(owner != nullptr, "owner must not be nullptr");
|
|
|
|
// See if we can find the name of the propertyowner to add
|
|
std::vector<PropertyOwner*>::const_iterator it = std::find_if(
|
|
_subOwners.begin(),
|
|
_subOwners.end(),
|
|
[identifier = owner->identifier()](PropertyOwner* o) {
|
|
return o->identifier() == identifier;
|
|
}
|
|
);
|
|
|
|
// If we found the propertyowner, we can delete it
|
|
if (it != _subOwners.end() && (*it)->identifier() == owner->identifier()) {
|
|
_subOwners.erase(it);
|
|
}
|
|
else {
|
|
LERROR(std::format(
|
|
"PropertyOwner with name '{}' not found for removal", owner->identifier()
|
|
));
|
|
}
|
|
}
|
|
|
|
void PropertyOwner::removePropertySubOwner(openspace::properties::PropertyOwner& owner) {
|
|
removePropertySubOwner(&owner);
|
|
}
|
|
|
|
void PropertyOwner::setIdentifier(std::string identifier) {
|
|
if (identifier.find_first_of(". \t\n") != std::string::npos) {
|
|
throw ghoul::RuntimeError("Identifier must not contain any dots or whitespaces");
|
|
}
|
|
_identifier = std::move(identifier);
|
|
}
|
|
|
|
const std::string& PropertyOwner::identifier() const {
|
|
return _identifier;
|
|
}
|
|
|
|
const std::string& PropertyOwner::type() const {
|
|
return _type;
|
|
}
|
|
|
|
void PropertyOwner::setGuiName(std::string guiName) {
|
|
_guiName = std::move(guiName);
|
|
}
|
|
|
|
const std::string& PropertyOwner::guiName() const {
|
|
return _guiName.empty() ? _identifier : _guiName;
|
|
}
|
|
|
|
void PropertyOwner::setDescription(std::string description) {
|
|
_description = std::move(description);
|
|
}
|
|
|
|
const std::string& PropertyOwner::description() const {
|
|
return _description;
|
|
}
|
|
|
|
const std::vector<std::string>& PropertyOwner::tags() const {
|
|
return _tags;
|
|
}
|
|
|
|
void PropertyOwner::addTag(std::string tag) {
|
|
_tags.push_back(std::move(tag));
|
|
}
|
|
|
|
void PropertyOwner::removeTag(const std::string& tag) {
|
|
_tags.erase(std::remove(_tags.begin(), _tags.end(), tag), _tags.end());
|
|
}
|
|
|
|
} // namespace openspace::properties
|