18 #include "exception.h" 19 #include "stringify.h" 21 #include <third_party/json/json.h> 28 #include <type_traits> 44 : parent_(parent), name_(name), description_(description) {
45 CHECK(parent !=
nullptr);
46 CHECK(!name_.empty());
47 CHECK(!description_.empty());
53 const string&
name()
const {
return name_; }
62 virtual string value()
const = 0;
65 virtual string defaultValue()
const = 0;
71 virtual void setValue(
const string& str) = 0;
78 virtual void copyFrom(
const Property& src) = 0;
81 virtual json toJson()
const = 0;
84 virtual void fromJson(
const json& json_obj) = 0;
90 auto typed_property =
dynamic_cast<const TypedProperty<T>*
>(
this);
91 if (typed_property ==
nullptr)
93 return typed_property->nativeValue();
99 return dynamic_cast<const TypedProperty<T>*
>(
this) !=
nullptr;
105 const string description_;
109 class TypedProperty :
public Property {
111 TypedProperty(PropertySet* parent,
115 const char* description)
116 : Property(parent, name, description),
117 default_value_(default_value),
122 string defaultValue()
const override {
return core::toString(default_value_); }
124 void setValue(
const string& str)
override;
126 vector<string>
knownValues()
const override {
return core::knownValues<T>(); }
128 void copyFrom(
const Property& src)
override;
130 json toJson()
const override {
return value(); }
132 void fromJson(
const json& json_obj)
override { setValue(json_obj); }
134 const T& nativeValue()
const {
return *value_; }
202 TAG
tag()
const {
return tag_; }
206 if (cases_.find(tag_) == cases_.end())
213 auto it = cases_.find(tag_);
214 CHECK(it != cases_.end());
220 auto it = cases_.find(tag_);
221 CHECK(it != cases_.end());
230 json json_obj = json::object();
232 for (
const auto& [case_tag, case_value] : cases_) {
245 CHECK(json_obj.is_object());
247 bool at_least_one_case =
false;
248 tag_ = core::fromString<TAG>(json_obj.at(
"tag"));
249 for (
auto& [case_tag, case_value] : cases_) {
251 if (json_it != json_obj.end()) {
252 case_value->fromJson(json_it.value());
253 at_least_one_case =
true;
255 case_value->resetToDefaultValues();
260 CHECK(cases_.empty() || at_least_one_case);
265 CHECK(
typeid(*
this) ==
typeid(src));
266 CHECK(cases_.size() == src.cases_.size());
268 for (
const auto& [case_tag, case_value] : src.cases_) {
269 cases_.at(case_tag)->copyFrom(*case_value);
274 bool registerCase(TAG tag,
PropertySet* property_set) {
275 CHECK(cases_.insert({ tag, property_set }).second,
"Duplicate cases");
282 map<TAG, PropertySet*> cases_;
286 class VariantProperty :
public Property {
287 using TagType =
typename T::TagType;
290 VariantProperty(PropertySet* parent,
292 TagType default_case,
294 const char* description)
295 : Property(parent, name, description),
296 default_case_(default_case),
299 string value()
const override {
return core::toString(variant_->tag()); }
301 string defaultValue()
const override {
return core::toString(default_case_); }
303 PropertySet* childPropertySet()
const override {
return variant_->activeCase(); }
305 void setValue(
const string& str)
override;
307 vector<string>
knownValues()
const override {
return core::knownValues<TagType>(); }
309 void copyFrom(
const Property& src)
override;
311 json toJson()
const override {
return variant_->toJson(); }
313 void fromJson(
const json& json_obj)
override { variant_->fromJson(json_obj); }
316 TagType default_case_;
317 T* variant_ =
nullptr;
395 vector<Property*> properties;
396 for (
const auto& property : properties_)
397 properties.push_back(property.get());
403 vector<const Property*> properties;
404 for (
const auto& property : properties_)
405 properties.push_back(property.get());
411 for (
const auto& property : properties_)
412 property->setValue(property->defaultValue());
417 CHECK(
typeid(*
this) ==
typeid(src),
"Incompatible property sets");
418 CHECK(properties_.size() == src.properties_.size());
421 throw core::Exception(
"Attempting to use 'copyFrom' on a sealed property set");
424 for (
size_t i = 0; i < properties_.size(); ++i)
425 properties_[i]->copyFrom(*src.properties_[i]);
430 void seal(
bool sealed =
true) {
440 throw core::Exception(
"Attempting to set property '%s' on a sealed property set",
451 json json_obj = json::object();
452 for (
const auto& property : properties_) {
453 json_obj[
property->name()] =
property->toJson();
465 CHECK(json_obj.is_object());
468 throw core::Exception(
"Attempting to use 'fromJson' on a sealed property set");
471 bool at_least_one_value =
false;
472 for (
const auto& property : properties_) {
473 auto json_it = json_obj.find(property->name());
474 if (json_it != json_obj.end()) {
475 property->fromJson(json_it.value());
476 at_least_one_value =
true;
478 property->setValue(property->defaultValue());
483 CHECK(json_obj.empty() || properties_.empty() || at_least_one_value);
488 T registerProperty(
const char* name,
491 const char* description) {
492 assert(name !=
nullptr);
493 assert(value !=
nullptr);
494 properties_.push_back(
495 make_unique<TypedProperty<T>>(
this, name, default_value, value, description));
496 return default_value;
499 template <
class V,
class TAG>
500 bool registerVariant(
const char* name,
503 const char* description) {
504 assert(name !=
nullptr);
505 assert(variant !=
nullptr);
506 variant->selectCase(default_case);
507 properties_.push_back(
508 make_unique<VariantProperty<V>>(
this, name, default_case, variant, description));
513 vector<unique_ptr<Property>> properties_;
514 bool sealed_ =
false;
518 void TypedProperty<T>::setValue(
const string& str) {
519 parent()->onPropertyChange(
this);
520 *value_ = core::fromString<T>(str);
524 void TypedProperty<T>::copyFrom(
const Property& src) {
525 parent()->onPropertyChange(
this);
526 *value_ = *
dynamic_cast<const TypedProperty&
>(src).value_;
530 void VariantProperty<T>::setValue(
const string& str) {
531 parent()->onPropertyChange(
this);
532 variant_->selectCase(core::fromString<TagType>(str));
536 void VariantProperty<T>::copyFrom(
const Property& src) {
537 parent()->onPropertyChange(
this);
538 variant_->copyFrom(*dynamic_cast<const VariantProperty&>(src).variant_);
542 #define PROPERTY(name, type, default_value, description) \ 543 type name { registerProperty<type>(#name, type(default_value), &name, (description)) } 546 #define VARIANT(name, type, default_case, description) \ 548 bool name##_INIT { registerVariant<type>(#name, (default_case), &name, (description)) } 551 #define CASE(tag, name, type) \ 553 bool name##_INIT { registerCase((tag), &name) } PropertySet * parent() const
The PropertySet which contains this property.
Definition: properties.h:59
The base for exception types in the Darwin framework.
Definition: exception.h:27
void onPropertyChange(const Property *property)
Called before updating a property value.
Definition: properties.h:438
Generic utilities.
Definition: exception.h:24
void fromJson(const json &json_obj)
Deserialize a set of properties from a json object.
Definition: properties.h:464
void fromJson(const json &json_obj)
Deserialize from a json object.
Definition: properties.h:244
bool isA() const
Returns true if the property wraps a T value.
Definition: properties.h:98
vector< const Property * > properties() const
Accessor to the list of properties.
Definition: properties.h:402
const T & nativeValue() const
Type-safe accessor to the native property value.
Definition: properties.h:89
void selectCase(TAG tag)
Selects the active PropertySet case.
Definition: properties.h:205
void copyFrom(const PropertySetVariant &src)
Transfer values between two property set variants.
Definition: properties.h:264
virtual PropertySet * childPropertySet() const
Return the optional child PropertySet, or nullptr
Definition: properties.h:68
TAG tag() const
The tag value indicating the active PropertySet case.
Definition: properties.h:202
json toJson() const
Serialize all the properties to a json object.
Definition: properties.h:450
bool sealed() const
Returns true if the property set is sealed.
Definition: properties.h:435
Reflection interface to a property in a PropertySet.
Definition: properties.h:41
const string & description() const
Property description.
Definition: properties.h:56
vector< Property * > properties()
Accessor to the list of properties.
Definition: properties.h:394
json toJson() const
Serialize to a json object.
Definition: properties.h:229
void copyFrom(const PropertySet &src)
Transfer values between two property sets.
Definition: properties.h:416
The foundation for data structures supporting runtime reflection.
Definition: properties.h:388
PropertySet * activeCase()
Returns the active PropertySet.
Definition: properties.h:219
Classes derived from this are not copyable or movable.
Definition: utils.h:69
A variant type (tagged-union) with PropertySet fields.
Definition: properties.h:194
void resetToDefaultValues()
Resets all the properties to the default values.
Definition: properties.h:410
string toString(const T &value)
Convenience helper to stringify a value.
Definition: stringify.h:172
vector< string > knownValues()
Convenience helper to return the set of known values.
Definition: stringify.h:184
const string & name() const
Property name.
Definition: properties.h:53
void seal(bool sealed=true)
After sealing a PropertySet, all attempts to modify it through the Property interface will throw an e...
Definition: properties.h:430
const PropertySet * activeCase() const
Returns the active PropertySet.
Definition: properties.h:212