From 1a6b41787a06342041b9b19b952bfb06d8cbb7f2 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 22 Sep 2015 09:42:30 +0200 Subject: [PATCH] Implement joins for status queries fixes #10060 --- lib/base/object.cpp | 5 +++ lib/base/object.hpp | 1 + lib/base/type.cpp | 6 +-- lib/base/type.hpp | 10 +++-- lib/icinga/checkable-check.cpp | 2 +- lib/icinga/checkable.ti | 26 +++++++++++-- lib/icinga/dependency.ti | 38 ++++++++++++++++--- lib/icinga/notification.ti | 34 ++++++++++++++--- lib/icinga/scheduleddowntime.ti | 15 +++++++- lib/icinga/service.hpp | 2 +- lib/icinga/service.ti | 6 +++ lib/icinga/user.ti | 7 +++- lib/remote/filterutility.cpp | 25 +++++++++--- lib/remote/statusqueryhandler.cpp | 48 +++++++++++++++++++---- lib/remote/typequeryhandler.cpp | 4 +- lib/remote/zone.ti | 7 +++- tools/mkclass/class_lexer.ll | 4 +- tools/mkclass/class_parser.yy | 43 +++++++++++++++------ tools/mkclass/classcompiler.cpp | 63 ++++++++++++++++++++++++++++++- tools/mkclass/classcompiler.hpp | 11 ++++-- 20 files changed, 301 insertions(+), 56 deletions(-) diff --git a/lib/base/object.cpp b/lib/base/object.cpp index 38fd82fd4..ca0cb540f 100644 --- a/lib/base/object.cpp +++ b/lib/base/object.cpp @@ -107,6 +107,11 @@ void Object::NotifyField(int id, const Value& cookie) BOOST_THROW_EXCEPTION(std::runtime_error("Invalid field ID.")); } +Object::Ptr Object::NavigateField(int id) const +{ + BOOST_THROW_EXCEPTION(std::runtime_error("Invalid field ID.")); +} + Object::Ptr Object::Clone(void) const { BOOST_THROW_EXCEPTION(std::runtime_error("Object cannot be cloned.")); diff --git a/lib/base/object.hpp b/lib/base/object.hpp index b56377bf5..12bd2d6c1 100644 --- a/lib/base/object.hpp +++ b/lib/base/object.hpp @@ -109,6 +109,7 @@ public: virtual Value GetField(int id) const; virtual void ValidateField(int id, const Value& value, const ValidationUtils& utils); virtual void NotifyField(int id, const Value& cookie = Empty); + virtual Object::Ptr NavigateField(int id) const; #ifdef I2_DEBUG bool OwnsLock(void) const; diff --git a/lib/base/type.cpp b/lib/base/type.cpp index 1b34c0f8c..b084cb807 100644 --- a/lib/base/type.cpp +++ b/lib/base/type.cpp @@ -169,11 +169,11 @@ Field TypeType::GetFieldInfo(int id) const return GetBaseType()->GetFieldInfo(id); if (id == 0) - return Field(0, "String", "name", NULL, 0, 0); + return Field(0, "String", "name", "", NULL, 0, 0); else if (id == 1) - return Field(1, "Object", "prototype", NULL, 0, 0); + return Field(1, "Object", "prototype", "", NULL, 0, 0); else if (id == 2) - return Field(2, "Type", "base", NULL, 0, 0); + return Field(2, "Type", "base", "", NULL, 0, 0); throw std::runtime_error("Invalid field ID."); } diff --git a/lib/base/type.hpp b/lib/base/type.hpp index 4084e8b99..04cfedb41 100644 --- a/lib/base/type.hpp +++ b/lib/base/type.hpp @@ -37,8 +37,9 @@ enum FieldAttribute FAConfig = 2, FAState = 4, FAInternal = 64, - FARequired = 512 -}; + FARequired = 512, + FANavigation = 1024 +}; class Type; @@ -47,12 +48,13 @@ struct Field int ID; const char *TypeName; const char *Name; + const char *NavigationName; const char *RefTypeName; int Attributes; int ArrayRank; - Field(int id, const char *type, const char *name, const char *reftype, int attributes, int arrayRank) - : ID(id), TypeName(type), Name(name), RefTypeName(reftype), Attributes(attributes), ArrayRank(arrayRank) + Field(int id, const char *type, const char *name, const char *navigationName, const char *reftype, int attributes, int arrayRank) + : ID(id), TypeName(type), Name(name), NavigationName(navigationName), RefTypeName(reftype), Attributes(attributes), ArrayRank(arrayRank) { } }; diff --git a/lib/icinga/checkable-check.cpp b/lib/icinga/checkable-check.cpp index 6cead527d..38bd0de1b 100644 --- a/lib/icinga/checkable-check.cpp +++ b/lib/icinga/checkable-check.cpp @@ -42,7 +42,7 @@ boost::signals2::signal(NavigateCheckCommandRaw()); } TimePeriod::Ptr Checkable::GetCheckPeriod(void) const diff --git a/lib/icinga/checkable.ti b/lib/icinga/checkable.ti index 787f92adf..afb5a7594 100644 --- a/lib/icinga/checkable.ti +++ b/lib/icinga/checkable.ti @@ -20,6 +20,8 @@ #include "icinga/icingaapplication.hpp" #include "icinga/customvarobject.hpp" #include "base/array.hpp" +#impl_include "icinga/checkcommand.hpp" +#impl_include "icinga/eventcommand.hpp" library icinga; @@ -42,18 +44,30 @@ enum AcknowledgementType abstract class Checkable : CustomVarObject { - [config, required] name(CheckCommand) check_command (CheckCommandRaw); + [config, required, navigation] name(CheckCommand) check_command (CheckCommandRaw) { + navigate {{{ + return CheckCommand::GetByName(GetCheckCommandRaw()); + }}} + }; [config] int max_check_attempts { default {{{ return 3; }}} }; - [config] name(TimePeriod) check_period (CheckPeriodRaw); + [config, navigation] name(TimePeriod) check_period (CheckPeriodRaw) { + navigate {{{ + return TimePeriod::GetByName(GetCheckPeriodRaw()); + }}} + }; [config] double check_interval { default {{{ return 5 * 60; }}} }; [config] double retry_interval { default {{{ return 60; }}} }; - [config] name(EventCommand) event_command (EventCommandRaw); + [config, navigation] name(EventCommand) event_command (EventCommandRaw) { + navigate {{{ + return EventCommand::GetByName(GetEventCommandRaw()); + }}} + }; [config] bool volatile; [config] double flapping_threshold { default {{{ return 30; }}} @@ -137,7 +151,11 @@ abstract class Checkable : CustomVarObject get {{{ return false; }}} }; - [config] name(Endpoint) command_endpoint (CommandEndpointRaw); + [config, navigation] name(Endpoint) command_endpoint (CommandEndpointRaw) { + navigate {{{ + return Endpoint::GetByName(GetCommandEndpointRaw()); + }}} + }; }; } diff --git a/lib/icinga/dependency.ti b/lib/icinga/dependency.ti index a15853042..86d2d8d04 100644 --- a/lib/icinga/dependency.ti +++ b/lib/icinga/dependency.ti @@ -40,8 +40,13 @@ class Dependency : CustomVarObject < DependencyNameComposer load_after Host; load_after Service; - [config, required] name(Host) child_host_name; - [config] String child_service_name { + [config, required, navigation(child_host)] name(Host) child_host_name { + navigate {{{ + return Host::GetByName(GetChildHostName()); + }}} + }; + + [config, navigation(child_service)] String child_service_name { track {{{ if (!oldValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetParentHostName(), oldValue); @@ -53,10 +58,22 @@ class Dependency : CustomVarObject < DependencyNameComposer DependencyGraph::RemoveDependency(this, service.get()); } }}} + navigate {{{ + if (GetChildServiceName().IsEmpty()) + return Service::Ptr(); + + Host::Ptr host = Host::GetByName(GetChildHostName()); + return host->GetServiceByShortName(GetChildServiceName()); + }}} }; - [config, required] name(Host) parent_host_name; - [config] String parent_service_name { + [config, required, navigation(parent_host)] name(Host) parent_host_name { + navigate {{{ + return Host::GetByName(GetParentHostName()); + }}} + }; + + [config, navigation(parent_service)] String parent_service_name { track {{{ if (!oldValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetParentHostName(), oldValue); @@ -68,9 +85,20 @@ class Dependency : CustomVarObject < DependencyNameComposer DependencyGraph::RemoveDependency(this, service.get()); } }}} + navigate {{{ + if (GetParentServiceName().IsEmpty()) + return Service::Ptr(); + + Host::Ptr host = Host::GetByName(GetParentHostName()); + return host->GetServiceByShortName(GetParentServiceName()); + }}} }; - [config] name(TimePeriod) period (PeriodRaw); + [config, navigation] name(TimePeriod) period (PeriodRaw) { + navigate {{{ + return TimePeriod::GetByName(GetPeriodRaw()); + }}} + }; [config] array(double) states; int state_filter_real (StateFilter); diff --git a/lib/icinga/notification.ti b/lib/icinga/notification.ti index dc83ce7b1..5df94ed1f 100644 --- a/lib/icinga/notification.ti +++ b/lib/icinga/notification.ti @@ -18,6 +18,7 @@ ******************************************************************************/ #include "icinga/customvarobject.hpp" +#impl_include "icinga/notificationcommand.hpp" #impl_include "icinga/service.hpp" library icinga; @@ -39,11 +40,19 @@ class Notification : CustomVarObject < NotificationNameComposer load_after Host; load_after Service; - [config, protected, required] name(NotificationCommand) command (CommandRaw); + [config, protected, required, navigation] name(NotificationCommand) command (CommandRaw) { + navigate {{{ + return NotificationCommand::GetByName(GetCommandRaw()); + }}} + }; [config] double interval { default {{{ return 1800; }}} }; - [config] name(TimePeriod) period (PeriodRaw); + [config, navigation] name(TimePeriod) period (PeriodRaw) { + navigate {{{ + return TimePeriod::GetByName(GetPeriodRaw()); + }}} + }; [config, protected] array(name(User)) users (UsersRaw); [config, protected] array(name(UserGroup)) user_groups (UserGroupsRaw); [config] Dictionary::Ptr times; @@ -51,8 +60,12 @@ class Notification : CustomVarObject < NotificationNameComposer int type_filter_real (TypeFilter); [config] array(double) states; int state_filter_real (StateFilter); - [config, protected, required] name(Host) host_name; - [config, protected] String service_name { + [config, protected, required, navigation(host)] name(Host) host_name { + navigate {{{ + return Host::GetByName(GetHostName()); + }}} + }; + [config, protected, navigation(service)] String service_name { track {{{ if (!oldValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetHostName(), oldValue); @@ -64,6 +77,13 @@ class Notification : CustomVarObject < NotificationNameComposer DependencyGraph::RemoveDependency(this, service.get()); } }}} + navigate {{{ + if (GetServiceName().IsEmpty()) + return Service::Ptr(); + + Host::Ptr host = Host::GetByName(GetHostName()); + return host->GetServiceByShortName(GetServiceName()); + }}} }; [state] Array::Ptr notified_users { @@ -75,7 +95,11 @@ class Notification : CustomVarObject < NotificationNameComposer [state, set_protected] Value notification_number; [state] double last_problem_notification; - [config] name(Endpoint) command_endpoint (CommandEndpointRaw); + [config, navigation] name(Endpoint) command_endpoint (CommandEndpointRaw) { + navigate {{{ + return Endpoint::GetByName(GetCommandEndpointRaw()); + }}} + }; }; validator Notification { diff --git a/lib/icinga/scheduleddowntime.ti b/lib/icinga/scheduleddowntime.ti index dedefc3dc..a398419aa 100644 --- a/lib/icinga/scheduleddowntime.ti +++ b/lib/icinga/scheduleddowntime.ti @@ -39,8 +39,12 @@ class ScheduledDowntime : CustomVarObject < ScheduledDowntimeNameComposer load_after Host; load_after Service; - [config, protected, required] name(Host) host_name; - [config, protected] String service_name { + [config, protected, required, navigation(host)] name(Host) host_name { + navigate {{{ + return Host::GetByName(GetHostName()); + }}} + }; + [config, protected, navigation(service)] String service_name { track {{{ if (!oldValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetHostName(), oldValue); @@ -52,6 +56,13 @@ class ScheduledDowntime : CustomVarObject < ScheduledDowntimeNameComposer DependencyGraph::RemoveDependency(this, service.get()); } }}} + navigate {{{ + if (GetServiceName().IsEmpty()) + return Service::Ptr(); + + Host::Ptr host = Host::GetByName(GetHostName()); + return host->GetServiceByShortName(GetServiceName()); + }}} }; [config, required] String author; diff --git a/lib/icinga/service.hpp b/lib/icinga/service.hpp index 08af8c34b..c863699ec 100644 --- a/lib/icinga/service.hpp +++ b/lib/icinga/service.hpp @@ -41,7 +41,7 @@ public: static Service::Ptr GetByNamePair(const String& hostName, const String& serviceName); - Host::Ptr GetHost(void) const; + virtual Host::Ptr GetHost(void) const override; virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, Value *result) const override; diff --git a/lib/icinga/service.ti b/lib/icinga/service.ti index d3f939bdc..2fcbcc39d 100644 --- a/lib/icinga/service.ti +++ b/lib/icinga/service.ti @@ -53,6 +53,12 @@ class Service : Checkable < ServiceNameComposer }}} }; [config, required] name(Host) host_name; + [no_storage, navigation] Host::Ptr host { + get; + navigate {{{ + return GetHost(); + }}} + }; [enum, no_storage] ServiceState "state" { get {{{ return GetStateRaw(); diff --git a/lib/icinga/user.ti b/lib/icinga/user.ti index a3940e323..5b90019ad 100644 --- a/lib/icinga/user.ti +++ b/lib/icinga/user.ti @@ -38,7 +38,12 @@ class User : CustomVarObject [config] array(name(UserGroup)) groups { default {{{ return new Array(); }}} }; - [config] name(TimePeriod) period (PeriodRaw); + [config, navigation] name(TimePeriod) period (PeriodRaw) { + navigate {{{ + return TimePeriod::GetByName(GetPeriodRaw()); + }}} + }; + [config] array(double) types; int type_filter_real (TypeFilter); [config] array(double) states; diff --git a/lib/remote/filterutility.cpp b/lib/remote/filterutility.cpp index 0b14dec6a..b91d4ce76 100644 --- a/lib/remote/filterutility.cpp +++ b/lib/remote/filterutility.cpp @@ -78,10 +78,28 @@ String ConfigObjectTargetProvider::GetPluralName(const String& type) const return Type::GetByName(type)->GetPluralName(); } -static void FilteredAddTarget(ScriptFrame& frame, const String& varName, Expression *ufilter, std::vector& result, const Value& target) +static void FilteredAddTarget(ScriptFrame& frame, Expression *ufilter, std::vector& result, const Object::Ptr& target) { + Type::Ptr type = target->GetReflectionType(); + String varName = type->GetName(); + boost::algorithm::to_lower(varName); + frame.Locals->Set(varName, target); + for (int fid = 0; fid < type->GetFieldCount(); fid++) { + Field field = type->GetFieldInfo(fid); + + if ((field.Attributes & FANavigation) == 0) + continue; + + Object::Ptr joinedObj = target->NavigateField(fid); + + varName = field.TypeName; + boost::algorithm::to_lower(varName); + + frame.Locals->Set(varName, joinedObj); + } + if (Convert::ToBool(ufilter->Evaluate(frame))) result.push_back(target); } @@ -153,11 +171,8 @@ std::vector FilterUtility::GetFilterTargets(const QueryDescription& qd, c } } - String varName = type; - boost::algorithm::to_lower(varName); - try { - provider->FindTargets(type, boost::bind(&FilteredAddTarget, boost::ref(frame), varName, ufilter, boost::ref(result), _1)); + provider->FindTargets(type, boost::bind(&FilteredAddTarget, boost::ref(frame), ufilter, boost::ref(result), _1)); } catch (const std::exception& ex) { delete ufilter; throw; diff --git a/lib/remote/statusqueryhandler.cpp b/lib/remote/statusqueryhandler.cpp index e94b01248..6a1134a87 100644 --- a/lib/remote/statusqueryhandler.cpp +++ b/lib/remote/statusqueryhandler.cpp @@ -46,8 +46,15 @@ bool StatusQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re QueryDescription qd; qd.Types.insert(type->GetName()); - std::vector joinTypes; - joinTypes.push_back(type->GetName()); + std::vector joinAttrs; + joinAttrs.push_back(""); + + for (int fid = 0; fid < type->GetFieldCount(); fid++) { + Field field = type->GetFieldInfo(fid); + + if (field.Attributes & FANavigation) + joinAttrs.push_back(field.Name); + } Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); @@ -80,17 +87,44 @@ bool StatusQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re Dictionary::Ptr resultAttrs = new Dictionary(); result1->Set("attrs", resultAttrs); - BOOST_FOREACH(const String& joinType, joinTypes) { - String prefix = joinType; + BOOST_FOREACH(const String& joinAttr, joinAttrs) { + Object::Ptr joinedObj; + String prefix; + + if (joinAttr.IsEmpty()) { + joinedObj = obj; + prefix = type->GetName(); + } else { + int fid = type->GetFieldId(joinAttr); + joinedObj = static_cast(obj)->NavigateField(fid); + + if (!joinedObj) + continue; + + Field field = type->GetFieldInfo(fid); + prefix = field.NavigationName; + } + boost::algorithm::to_lower(prefix); - for (int fid = 0; fid < type->GetFieldCount(); fid++) { - Field field = type->GetFieldInfo(fid); + Type::Ptr joinedType = joinedObj->GetReflectionType(); + + for (int fid = 0; fid < joinedType->GetFieldCount(); fid++) { + Field field = joinedType->GetFieldInfo(fid); String aname = prefix + "." + field.Name; if (!attrs.empty() && attrs.find(aname) == attrs.end()) continue; - Value val = static_cast(obj)->GetField(fid); + Value val = joinedObj->GetField(fid); + + /* hide internal navigation fields */ + if (field.Attributes & FANavigation) { + Value nval = joinedObj->NavigateField(fid); + + if (val == nval) + continue; + } + Value sval = Serialize(val, FAConfig | FAState); resultAttrs->Set(aname, sval); } diff --git a/lib/remote/typequeryhandler.cpp b/lib/remote/typequeryhandler.cpp index 339347afb..5786e4192 100644 --- a/lib/remote/typequeryhandler.cpp +++ b/lib/remote/typequeryhandler.cpp @@ -138,6 +138,8 @@ bool TypeQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& requ fieldInfo->Set("type", field.TypeName); if (field.RefTypeName) fieldInfo->Set("ref_type", field.RefTypeName); + if (field.Attributes & FANavigation) + fieldInfo->Set("navigation_name", field.NavigationName); fieldInfo->Set("array_rank", field.ArrayRank); Dictionary::Ptr attributeInfo = new Dictionary(); @@ -147,7 +149,7 @@ bool TypeQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& requ attributeInfo->Set("state", static_cast(field.Attributes & FAState)); attributeInfo->Set("internal", static_cast(field.Attributes & FAInternal)); attributeInfo->Set("required", static_cast(field.Attributes & FARequired)); - + attributeInfo->Set("navigation", static_cast(field.Attributes & FANavigation)); } } diff --git a/lib/remote/zone.ti b/lib/remote/zone.ti index 86296aa92..717283ef6 100644 --- a/lib/remote/zone.ti +++ b/lib/remote/zone.ti @@ -26,7 +26,12 @@ namespace icinga class Zone : ConfigObject { - [config] name(Zone) parent (ParentRaw); + [config, navigation] name(Zone) parent (ParentRaw) { + navigate {{{ + return Zone::GetByName(GetParentRaw()); + }}} + }; + [config] array(name(Endpoint)) endpoints (EndpointsRaw); [config] bool global; }; diff --git a/tools/mkclass/class_lexer.ll b/tools/mkclass/class_lexer.ll index fb9c88ae4..d339c11c9 100644 --- a/tools/mkclass/class_lexer.ll +++ b/tools/mkclass/class_lexer.ll @@ -145,6 +145,7 @@ set_protected { yylval->num = FASetProtected; return T_FIELD_ATTRIBUTE; } protected { yylval->num = FAGetProtected | FASetProtected; return T_FIELD_ATTRIBUTE; } internal { yylval->num = FAInternal; return T_FIELD_ATTRIBUTE; } no_storage { yylval->num = FANoStorage; return T_FIELD_ATTRIBUTE; } +navigation { return T_NAVIGATION; } validator { return T_VALIDATOR; } required { return T_REQUIRED; } name { return T_NAME; } @@ -153,8 +154,9 @@ default { yylval->num = FTDefault; return T_FIELD_ACCESSOR_TYPE; } get { yylval->num = FTGet; return T_FIELD_ACCESSOR_TYPE; } set { yylval->num = FTSet; return T_FIELD_ACCESSOR_TYPE; } track { yylval->num = FTTrack; return T_FIELD_ACCESSOR_TYPE; } +navigate { yylval->num = FTNavigate; return T_FIELD_ACCESSOR_TYPE; } \"[^\"]+\" { yylval->text = strdup(yytext + 1); yylval->text[strlen(yylval->text) - 1] = '\0'; return T_STRING; } -\<[^>]+\> { yylval->text = strdup(yytext + 1); yylval->text[strlen(yylval->text) - 1] = '\0'; return T_ANGLE_STRING; } +\<[^ \>]*\> { yylval->text = strdup(yytext + 1); yylval->text[strlen(yylval->text) - 1] = '\0'; return T_ANGLE_STRING; } [a-zA-Z_][:a-zA-Z0-9\-_]* { yylval->text = strdup(yytext); return T_IDENTIFIER; } . return yytext[0]; diff --git a/tools/mkclass/class_parser.yy b/tools/mkclass/class_parser.yy index a17c0f82c..777b2734b 100644 --- a/tools/mkclass/class_parser.yy +++ b/tools/mkclass/class_parser.yy @@ -65,6 +65,7 @@ using namespace icinga; %token T_NAMESPACE "namespace (T_NAMESPACE)" %token T_VALIDATOR "validator (T_VALIDATOR)" %token T_REQUIRED "required (T_REQUIRED)" +%token T_NAVIGATION "navigation (T_NAVIGATION)" %token T_NAME "name (T_NAME)" %token T_ARRAY "array (T_ARRAY)" %token T_STRING "string (T_STRING)" @@ -89,9 +90,9 @@ using namespace icinga; %type angle_impl_include %type code %type T_FIELD_ATTRIBUTE -%type field_attribute -%type field_attributes -%type field_attribute_list +%type field_attribute +%type field_attributes +%type field_attribute_list %type T_FIELD_ACCESSOR_TYPE %type T_CLASS_ATTRIBUTE %type class_attribute_list @@ -328,9 +329,7 @@ field_type: identifier class_field: field_attribute_list field_type identifier alternative_name_specifier field_accessor_list ';' { - Field *field = new Field(); - - field->Attributes = $1; + Field *field = $1; if ((field->Attributes & (FAConfig | FAState)) == 0) field->Attributes |= FAEphemeral; @@ -363,6 +362,10 @@ class_field: field_attribute_list field_type identifier alternative_name_specifi case FTTrack: field->TrackAccessor = it->Accessor; break; + case FTNavigate: + field->NavigateAccessor = it->Accessor; + field->PureNavigateAccessor = it->Pure; + break; } } @@ -392,7 +395,7 @@ alternative_name_specifier: /* empty */ field_attribute_list: /* empty */ { - $$ = 0; + $$ = new Field(); } | '[' field_attributes ']' { @@ -402,21 +405,39 @@ field_attribute_list: /* empty */ field_attribute: T_FIELD_ATTRIBUTE { - $$ = $1; + $$ = new Field(); + $$->Attributes = $1; } | T_REQUIRED { - $$ = FARequired; + $$ = new Field(); + $$->Attributes = FARequired; + } + | T_NAVIGATION '(' identifier ')' + { + $$ = new Field(); + $$->Attributes = FANavigation; + $$->NavigationName = $3; + std::free($3); + } + | T_NAVIGATION + { + $$ = new Field(); + $$->Attributes = FANavigation; } ; field_attributes: /* empty */ { - $$ = 0; + $$ = new Field(); } | field_attributes ',' field_attribute { - $$ = $1 | $3; + $$ = $1; + $$->Attributes |= $3->Attributes; + if (!$3->NavigationName.empty()) + $$->NavigationName = $3->NavigationName; + delete $3; } | field_attribute { diff --git a/tools/mkclass/classcompiler.cpp b/tools/mkclass/classcompiler.cpp index 91d035869..9382f99b1 100644 --- a/tools/mkclass/classcompiler.cpp +++ b/tools/mkclass/classcompiler.cpp @@ -366,7 +366,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) nameref = "NULL"; m_Impl << "\t\t" << "case " << num << ":" << std::endl - << "\t\t\t" << "return Field(" << num << ", \"" << ftype << "\", \"" << it->Name << "\", " << nameref << ", " << it->Attributes << ", " << it->Type.ArrayRank << ");" << std::endl; + << "\t\t\t" << "return Field(" << num << ", \"" << ftype << "\", \"" << it->Name << "\", \"" << (it->NavigationName.empty() ? it->Name : it->NavigationName) << "\", " << nameref << ", " << it->Attributes << ", " << it->Type.ArrayRank << ");" << std::endl; num++; } @@ -695,6 +695,42 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) m_Impl << "}" << std::endl << std::endl; + /* NavigateField */ + m_Header << "protected:" << std::endl + << "\t" << "virtual Object::Ptr NavigateField(int id) const override;" << std::endl; + + m_Impl << "Object::Ptr ObjectImpl<" << klass.Name << ">::NavigateField(int id) const" << std::endl + << "{" << std::endl; + + if (!klass.Parent.empty()) + m_Impl << "\t" << "int real_id = id - " << klass.Parent << "::TypeInstance->GetFieldCount(); " << std::endl + << "\t" << "if (real_id < 0) { return " << klass.Parent << "::NavigateField(id); }" << std::endl; + + m_Impl << "\t" << "switch ("; + + if (!klass.Parent.empty()) + m_Impl << "real_id"; + else + m_Impl << "id"; + + m_Impl << ") {" << std::endl; + + num = 0; + for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { + if (it->Attributes & FANavigation) { + m_Impl << "\t\t" << "case " << num << ":" << std::endl + << "\t\t\t" << "return Navigate" << it->GetFriendlyName() << "();" << std::endl; + } + + num++; + } + + m_Impl << "\t\t" << "default:" << std::endl + << "\t\t\t" << "throw std::runtime_error(\"Invalid field ID.\");" << std::endl + << "\t" << "}" << std::endl; + + m_Impl << "}" << std::endl << std::endl; + /* getters */ for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { std::string prot; @@ -811,6 +847,31 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) m_Impl << "}" << std::endl << std::endl; } + /* navigation */ + for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { + if ((it->Attributes & FANavigation) == 0) + continue; + + m_Header << "public:" << std::endl + << "\t" << "virtual Object::Ptr Navigate" << it->GetFriendlyName() << "(void) const"; + + if (it->PureNavigateAccessor) { + m_Header << " = 0;" << std::endl; + } else { + m_Header << ";" << std::endl; + + m_Impl << "Object::Ptr ObjectImpl<" << klass.Name << ">::Navigate" << it->GetFriendlyName() << "(void) const" << std::endl + << "{" << std::endl; + + if (it->NavigateAccessor.empty()) + m_Impl << "\t" << "return Get" << it->GetFriendlyName() << "();" << std::endl; + else + m_Impl << "\t" << it->NavigateAccessor << std::endl; + + m_Impl << "}" << std::endl << std::endl; + } + } + /* start/stop */ if (needs_tracking) { m_Header << "virtual void Start(void) override;" << std::endl diff --git a/tools/mkclass/classcompiler.hpp b/tools/mkclass/classcompiler.hpp index db845afc4..fca2f6a62 100644 --- a/tools/mkclass/classcompiler.hpp +++ b/tools/mkclass/classcompiler.hpp @@ -43,7 +43,8 @@ enum FieldAccessorType FTGet, FTSet, FTDefault, - FTTrack + FTTrack, + FTNavigate }; struct FieldAccessor @@ -69,7 +70,8 @@ enum FieldAttribute FAInternal = 64, FANoStorage = 128, FALoadDependency = 256, - FARequired = 512 + FARequired = 512, + FANavigation = 1024 }; struct FieldType @@ -116,9 +118,12 @@ struct Field bool PureSetAccessor; std::string DefaultAccessor; std::string TrackAccessor; + std::string NavigationName; + std::string NavigateAccessor; + bool PureNavigateAccessor; Field(void) - : Attributes(0), PureGetAccessor(false), PureSetAccessor(false) + : Attributes(0), PureGetAccessor(false), PureSetAccessor(false), PureNavigateAccessor(false) { } inline std::string GetFriendlyName(void) const