]> granicus.if.org Git - icinga2/commitdiff
Implement joins for status queries
authorGunnar Beutner <gunnar@beutner.name>
Tue, 22 Sep 2015 07:42:30 +0000 (09:42 +0200)
committerGunnar Beutner <gunnar@beutner.name>
Tue, 22 Sep 2015 07:45:23 +0000 (09:45 +0200)
fixes #10060

20 files changed:
lib/base/object.cpp
lib/base/object.hpp
lib/base/type.cpp
lib/base/type.hpp
lib/icinga/checkable-check.cpp
lib/icinga/checkable.ti
lib/icinga/dependency.ti
lib/icinga/notification.ti
lib/icinga/scheduleddowntime.ti
lib/icinga/service.hpp
lib/icinga/service.ti
lib/icinga/user.ti
lib/remote/filterutility.cpp
lib/remote/statusqueryhandler.cpp
lib/remote/typequeryhandler.cpp
lib/remote/zone.ti
tools/mkclass/class_lexer.ll
tools/mkclass/class_parser.yy
tools/mkclass/classcompiler.cpp
tools/mkclass/classcompiler.hpp

index 38fd82fd461342a92c8190092189fc76ddd0f276..ca0cb540f36ad426ef3c752d1199a503a68dda21 100644 (file)
@@ -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."));
index b56377bf52dfefbeb62737beffab39d237686f53..12bd2d6c18cb24586badaf5b5142434670bd2260 100644 (file)
@@ -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;
index 1b34c0f8cbd66f87bb64e509e5ff55146693b88f..b084cb80720d61f8277072e59ad37724033a0530 100644 (file)
@@ -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.");
 }
index 4084e8b998e0820340910cf5b21a285d954191f1..04cfedb4150f0881eae10b5c4c6cc48cade07171 100644 (file)
@@ -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)
        { }
 };
 
index 6cead527d014f72a6c743f6c844d602d782c975c..38bd0de1b710c11d727ffe327499b48aab294616 100644 (file)
@@ -42,7 +42,7 @@ boost::signals2::signal<void (const Checkable::Ptr&, NotificationType, const Che
 
 CheckCommand::Ptr Checkable::GetCheckCommand(void) const
 {
-       return CheckCommand::GetByName(GetCheckCommandRaw());
+       return dynamic_pointer_cast<CheckCommand>(NavigateCheckCommandRaw());
 }
 
 TimePeriod::Ptr Checkable::GetCheckPeriod(void) const
index 787f92adfc9d7ae317744743c55357ed73d7e78b..afb5a75940eda31c7a5ae609dac21ac3453b59c7 100644 (file)
@@ -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());
+               }}}
+       };
 };
 
 }
index a158530424a95b9e766c4c93b24e8996480555e7..86d2d8d04b97d872cec346be619a163b9cb77269 100644 (file)
@@ -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, navigation(parent_host)] name(Host) parent_host_name {
+               navigate {{{
+                       return Host::GetByName(GetParentHostName());
+               }}}
        };
 
-       [config, required] name(Host) parent_host_name;
-       [config] String parent_service_name {
+       [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);
index dc83ce7b162c926459d155bbfa068e0ad7d296e7..5df94ed1f61275bfc943093da8be587e95dac63f 100644 (file)
@@ -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 {
index dedefc3dc7436f1c1a0531fdbaaea7d29aa5e3a7..a398419aac5daea1de66de54690672b482fa8efa 100644 (file)
@@ -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;
index 08af8c34b12e5ee27e9bc9a8975c2fe9a0f4536a..c863699ecb4b22180336c6628cfa54cbbb89fac5 100644 (file)
@@ -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;
 
index d3f939bdc4d00e78b03a5454eb1086ccc4c2883b..2fcbcc39da41e1123b8ec5b37e680e89dec8edfc 100644 (file)
@@ -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();
index a3940e323a27bfff14e44f771aaee264e0140317..5b90019adba34660c6d08a9d5072f46205ea68f9 100644 (file)
@@ -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;
index 0b14dec6a50ef159e9df22e119a5cf9b78ba89d0..b91d4ce76c8329ff320ef6920ae24f6d949d8209 100644 (file)
@@ -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<Value>& result, const Value& target)
+static void FilteredAddTarget(ScriptFrame& frame, Expression *ufilter, std::vector<Value>& 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<Value> 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;
index e94b0124892245c02dc8aaaf4501a22846dc2fe4..6a1134a87b5fcefe32283153d42bd9a3ac855026 100644 (file)
@@ -46,8 +46,15 @@ bool StatusQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re
        QueryDescription qd;
        qd.Types.insert(type->GetName());
 
-       std::vector<String> joinTypes;
-       joinTypes.push_back(type->GetName());
+       std::vector<String> 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::algorithm::to_lower(prefix);
+               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<Object::Ptr>(obj)->NavigateField(fid);
+
+                               if (!joinedObj)
+                                       continue;
 
-                       for (int fid = 0; fid < type->GetFieldCount(); fid++) {
                                Field field = type->GetFieldInfo(fid);
+                               prefix = field.NavigationName;
+                       }
+
+                       boost::algorithm::to_lower(prefix);
+
+                       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<Object::Ptr>(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);
                        }
index 339347afbb222776c07503ab2b4234c597605dcb..5786e4192d078f391b4bbe966daa55b666d91347 100644 (file)
@@ -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<bool>(field.Attributes & FAState));
                        attributeInfo->Set("internal", static_cast<bool>(field.Attributes & FAInternal));
                        attributeInfo->Set("required", static_cast<bool>(field.Attributes & FARequired));
-
+                       attributeInfo->Set("navigation", static_cast<bool>(field.Attributes & FANavigation));
                }
        }
 
index 86296aa925949822fd864ff1dd033e2788023db6..717283ef6879da9e25786f981f6f548f59294f09 100644 (file)
@@ -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;
 };
index fb9c88ae46e104fcc6ff70208045cb4c7adfd5e0..d339c11c930b8cc42d745592905f66d223486f74 100644 (file)
@@ -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];
index a17c0f82c0b7456f1d59131423f66a8b2e2676c1..777b2734b89eba700fc5a2ae59ac61e8830399e1 100644 (file)
@@ -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 <text> angle_impl_include
 %type <text> code
 %type <num> T_FIELD_ATTRIBUTE
-%type <num> field_attribute
-%type <num> field_attributes
-%type <num> field_attribute_list
+%type <field> field_attribute
+%type <field> field_attributes
+%type <field> field_attribute_list
 %type <num> T_FIELD_ACCESSOR_TYPE
 %type <num> T_CLASS_ATTRIBUTE
 %type <num> 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
        {
index 91d035869cce1efbe85bae58e107d5542dbc7210..9382f99b1ea170333b1dd825ae1f6dc594b5419e 100644 (file)
@@ -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
index db845afc491c85c6497213f531c968797b66fcf2..fca2f6a6227aa0c334a99d971edd2e9abbe7e413 100644 (file)
@@ -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