Ram Gandikota / Mbed OS ABCD
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers m2mresourceinstance.cpp Source File

m2mresourceinstance.cpp

00001 /*
00002  * Copyright (c) 2015 ARM Limited. All rights reserved.
00003  * SPDX-License-Identifier: Apache-2.0
00004  * Licensed under the Apache License, Version 2.0 (the License); you may
00005  * not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  * http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
00012  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 #include <stdlib.h>
00017 #include "mbed-client/m2mresource.h"
00018 #include "mbed-client/m2mconstants.h"
00019 #include "mbed-client/m2mobservationhandler.h"
00020 #include "mbed-client/m2mobject.h"
00021 #include "mbed-client/m2mobjectinstance.h"
00022 #include "include/m2mreporthandler.h"
00023 #include "include/nsdllinker.h"
00024 #include "mbed-client/m2mblockmessage.h"
00025 #include "mbed-trace/mbed_trace.h"
00026 
00027 #define TRACE_GROUP "mClt"
00028 
00029 M2MResourceInstance::M2MResourceInstance(M2MResource &parent,
00030                                          const String &res_name,
00031                                          const String &resource_type,
00032                                          M2MResourceInstance::ResourceType type,
00033                                          const uint16_t object_instance_id,
00034                                          char* path,
00035                                          bool external_blockwise_store)
00036 : M2MBase(res_name,
00037           M2MBase::Dynamic,
00038           resource_type,
00039           path,
00040           external_blockwise_store),
00041  _parent_resource(parent),
00042  _value(NULL),
00043  _value_length(0),
00044  _block_message_data(NULL),
00045  _execute_callback(NULL),
00046  _resource_callback(NULL),
00047  _execute_function_pointer(NULL),
00048  _notification_sent_function_pointer(NULL),
00049  _incoming_block_message_cb(NULL),
00050  _outgoing_block_message_cb(NULL),
00051  _notification_sent_callback(NULL),
00052  _object_instance_id(object_instance_id),
00053  _resource_type(type)
00054 {
00055     M2MBase::set_base_type(M2MBase::ResourceInstance);
00056 }
00057 
00058 M2MResourceInstance::M2MResourceInstance(M2MResource &parent,
00059                                          const String &res_name,
00060                                          const String &resource_type,
00061                                          M2MResourceInstance::ResourceType type,
00062                                          const uint8_t *value,
00063                                          const uint8_t value_length,
00064                                          const uint16_t object_instance_id,
00065                                          char* path,
00066                                          bool external_blockwise_store)
00067 : M2MBase(res_name,
00068           M2MBase::Static,
00069           resource_type,
00070           path,
00071           external_blockwise_store),
00072  _parent_resource(parent),
00073  _value(NULL),
00074  _value_length(0),
00075  _block_message_data(NULL),
00076  _execute_callback(NULL),
00077  _resource_callback(NULL),
00078  _execute_function_pointer(NULL),
00079  _notification_sent_function_pointer(NULL),
00080  _incoming_block_message_cb(NULL),
00081  _outgoing_block_message_cb(NULL),
00082  _notification_sent_callback(NULL),
00083  _object_instance_id(object_instance_id),
00084   _resource_type(type)
00085 {
00086     M2MBase::set_base_type(M2MBase::Resource);
00087     if (mode() == M2MBase::Dynamic) {
00088         if( value != NULL && value_length > 0 ) {
00089             _value = alloc_string_copy(value, value_length);
00090             if(_value) {
00091                 _value_length = value_length;
00092             }
00093         }
00094     }
00095     // Copy resource value to struct since static resources are handled in mbed-client-c
00096     else if (mode() == M2MBase::Static) {
00097        sn_nsdl_dynamic_resource_parameters_s* res = get_nsdl_resource();
00098        sn_nsdl_static_resource_parameters_s* params = (sn_nsdl_static_resource_parameters_s*)res->static_resource_parameters;
00099        params->resource = alloc_string_copy(value, value_length);
00100        params->resourcelen = value_length;
00101     }
00102     else {
00103         // Directory, not supported
00104     }
00105 }
00106 
00107 M2MResourceInstance::M2MResourceInstance(M2MResource &parent,
00108                                          const lwm2m_parameters_s* s,
00109                                          M2MResourceInstance::ResourceType type,
00110                                          const uint16_t object_instance_id)
00111 : M2MBase(s),
00112   _parent_resource(parent),
00113   _value(NULL),
00114   _value_length(0),
00115   _block_message_data(NULL),
00116   _execute_callback(NULL),
00117   _resource_callback(NULL),
00118   _execute_function_pointer(NULL),
00119   _notification_sent_function_pointer(NULL),
00120   _incoming_block_message_cb(NULL),
00121   _outgoing_block_message_cb(NULL),
00122   _notification_sent_callback(NULL),
00123   _object_instance_id(object_instance_id),
00124   _resource_type(type)
00125 {
00126     //TBD: put to flash, or parse from the uri_path!!!!
00127     //same for the _object_instance_id.
00128     // TBD: we dont need _value here, because in c-struct there is resource field!!!!
00129     if( s->dynamic_resource_params->static_resource_parameters->resource != NULL &&
00130             s->dynamic_resource_params->static_resource_parameters->resourcelen > 0 ) {
00131         _value = alloc_string_copy(s->dynamic_resource_params->static_resource_parameters->resource,
00132                                    s->dynamic_resource_params->static_resource_parameters->resourcelen);
00133         if(_value) {
00134             _value_length = s->dynamic_resource_params->static_resource_parameters->resourcelen;
00135         }
00136     }
00137     //M2MBase::set_base_type(M2MBase::ResourceInstance);
00138 }
00139 
00140 M2MResourceInstance::~M2MResourceInstance()
00141 {
00142     free(_value);
00143     delete _execute_function_pointer;
00144     delete _execute_callback;
00145     delete _notification_sent_function_pointer;
00146     delete _incoming_block_message_cb;
00147     delete _outgoing_block_message_cb;
00148     delete _notification_sent_callback;
00149     delete _block_message_data;
00150 }
00151 
00152 M2MBase::BaseType M2MResourceInstance::base_type() const
00153 {
00154     return M2MBase::base_type();
00155 }
00156 
00157 M2MResourceInstance::ResourceType M2MResourceInstance::resource_instance_type() const
00158 {
00159     return _resource_type;
00160 }
00161 
00162 bool M2MResourceInstance::handle_observation_attribute(const char *query)
00163 {
00164     tr_debug("M2MResourceInstance::handle_observation_attribute - is_under_observation(%d)", is_under_observation());
00165     bool success = false;
00166 
00167     M2MReportHandler *handler = M2MBase::report_handler();
00168     if (!handler) {
00169         handler = M2MBase::create_report_handler();
00170     }
00171 
00172     if (handler) {
00173         success = handler->parse_notification_attribute(query,
00174                 M2MBase::base_type(), _resource_type);
00175         if(success) {
00176             if (is_under_observation()) {
00177                 handler->set_under_observation(true);
00178             }
00179         } else {
00180             handler->set_default_values();
00181         }
00182     }
00183     return success;
00184 }
00185 
00186 void M2MResourceInstance::set_execute_function(execute_callback  callback)
00187 {
00188     delete _execute_callback;
00189     _execute_callback = new execute_callback (callback);
00190 }
00191 
00192 void M2MResourceInstance::set_execute_function(execute_callback_2 callback)
00193 {
00194     delete _execute_function_pointer;
00195 
00196     _execute_function_pointer = new FP1<void, void*> (callback);
00197     set_execute_function(execute_callback (_execute_function_pointer, &FP1<void, void*>::call));
00198 }
00199 
00200 void M2MResourceInstance::clear_value()
00201 {
00202     tr_debug("M2MResourceInstance::clear_value");
00203 
00204      free(_value);
00205      _value = NULL;
00206      _value_length = 0;
00207 
00208     report();
00209 }
00210 
00211 bool M2MResourceInstance::set_value(int64_t value)
00212 {
00213     bool success;
00214     // max len of "-9223372036854775808" plus zero termination
00215     char buffer[20+1];
00216     uint32_t size = m2m::itoa_c(value, buffer);
00217 
00218     success = set_value((const uint8_t*)buffer, size);
00219 
00220     return success;
00221 }
00222 
00223 bool M2MResourceInstance::set_value(const uint8_t *value,
00224                                     const uint32_t value_length)
00225 {
00226     tr_debug("M2MResourceInstance::set_value()");
00227     bool success = false;
00228     bool value_changed = false;
00229     if(is_value_changed(value,value_length)) {
00230         value_changed = true;
00231     }
00232     if( value != NULL && value_length > 0 ) {
00233         success = true;
00234 
00235         free(_value);
00236         _value_length = 0;
00237 
00238         _value = alloc_string_copy(value, value_length);
00239         if(_value) {
00240             _value_length = value_length;
00241             if( value_changed ) { //
00242                 if (_resource_type == M2MResourceInstance::STRING) {
00243                     M2MReportHandler *report_handler = M2MBase::report_handler();
00244                     if(report_handler && is_under_observation()) {
00245                         report_handler->set_notification_trigger();
00246                     }
00247                 }
00248                 else {
00249                     report();
00250                 }
00251             }
00252         }
00253     }
00254     return success;
00255 }
00256 
00257 void M2MResourceInstance::report()
00258 {
00259     tr_debug("M2MResourceInstance::report()");
00260     M2MBase::Observation  observation_level = M2MBase::observation_level();
00261     tr_debug("M2MResourceInstance::report() - level %d", observation_level);
00262     if((M2MBase::O_Attribute & observation_level) == M2MBase::O_Attribute ||
00263        (M2MBase::OI_Attribute & observation_level) == M2MBase::OI_Attribute) {
00264         tr_debug("M2MResourceInstance::report() -- object/instance level");
00265         M2MObjectInstance& object_instance = get_parent_resource().get_parent_object_instance();
00266         object_instance.notification_update(observation_level);
00267     }
00268 
00269     if(M2MBase::Dynamic == mode() &&
00270        (M2MBase::R_Attribute & observation_level) == M2MBase::R_Attribute) {
00271         tr_debug("M2MResourceInstance::report() - resource level");
00272         if(!_resource_callback && _resource_type != M2MResourceInstance::STRING) {
00273             M2MReportHandler *report_handler = M2MBase::report_handler();
00274             if (report_handler && is_observable()) {
00275                 if(_value) {
00276                     report_handler->set_value(atof((const char*)_value));
00277                 } else {
00278                     report_handler->set_value(0);
00279                 }
00280             }
00281         }
00282         else {
00283             if (_resource_callback && base_type() == M2MBase::ResourceInstance) {
00284                 _resource_callback->notification_update();
00285             }
00286         }
00287     } else if(M2MBase::Static == mode()) {
00288         M2MObservationHandler *observation_handler = M2MBase::observation_handler();
00289         if(observation_handler) {
00290             observation_handler->value_updated(this);
00291         }
00292     } else {
00293         tr_debug("M2MResourceInstance::report() - mode = %d, is_observable = %d", mode(), is_observable());
00294     }
00295 }
00296 
00297 bool M2MResourceInstance::is_value_changed(const uint8_t* value, const uint32_t value_len)
00298 {
00299     bool changed = false;
00300     if(value_len != _value_length) {
00301         changed = true;
00302     } else if(value && !_value) {
00303         changed = true;
00304     } else if(_value && !value) {
00305         changed = true;
00306     } else {
00307         if (_value) {
00308             if (strcmp((char*)value, (char*)_value) != 0) {
00309                 changed = true;
00310             }
00311         }
00312     }
00313     tr_debug("M2MResourceInstance::is_value_changed() -- %s", changed ? "true" : "false");
00314     return changed;
00315 }
00316 
00317 void M2MResourceInstance::execute(void *arguments)
00318 {
00319     tr_debug("M2MResourceInstance::execute");
00320     if(_execute_callback) {
00321         (*_execute_callback)(arguments);
00322     }
00323 }
00324 
00325 void M2MResourceInstance::get_value(uint8_t *&value, uint32_t &value_length)
00326 {
00327     value_length = 0;
00328     if(value) {
00329         free(value);
00330         value = NULL;
00331     }
00332     if(_value && _value_length > 0) {
00333         value = alloc_string_copy(_value, _value_length);
00334         if(value) {
00335             value_length = _value_length;
00336         }
00337     }
00338 }
00339 
00340 int M2MResourceInstance::get_value_int()
00341 {
00342     int value_int = 0;
00343     // Get the value and convert it into integer. This is not the most
00344     // efficient way, as it takes pointless heap copy to get the zero termination.
00345     uint8_t* buffer = NULL;
00346     uint32_t length;
00347     get_value(buffer,length);
00348     if(buffer) {
00349         value_int = atoi((const char*)buffer);
00350         free(buffer);
00351     }
00352     return value_int;
00353 }
00354 
00355 String M2MResourceInstance::get_value_string() const
00356 {
00357     // XXX: do a better constructor to avoid pointless malloc
00358     String value;
00359     if (_value) {
00360         value.append_raw((char*)_value, _value_length);
00361     }
00362 
00363     return value;
00364 }
00365 
00366 uint8_t* M2MResourceInstance::value() const
00367 {
00368     return _value;
00369 }
00370 
00371 uint32_t M2MResourceInstance::value_length() const
00372 {
00373     return _value_length;
00374 }
00375 
00376 sn_coap_hdr_s* M2MResourceInstance::handle_get_request(nsdl_s *nsdl,
00377                                                sn_coap_hdr_s *received_coap_header,
00378                                                M2MObservationHandler *observation_handler)
00379 {
00380     tr_debug("M2MResourceInstance::handle_get_request()");
00381     sn_coap_msg_code_e msg_code = COAP_MSG_CODE_RESPONSE_CONTENT;
00382     sn_coap_hdr_s *coap_response = sn_nsdl_build_response(nsdl,
00383                                                           received_coap_header,
00384                                                           msg_code);
00385     if(received_coap_header) {
00386         // process the GET if we have registered a callback for it
00387         if ((operation() & SN_GRS_GET_ALLOWED) != 0) {
00388             if(coap_response) {
00389                 if(_resource_type == M2MResourceInstance::OPAQUE) {
00390                     coap_response->content_format = sn_coap_content_format_e(COAP_CONTENT_OMA_OPAQUE_TYPE);
00391                 } else {
00392                     coap_response->content_format = sn_coap_content_format_e(0);
00393                 }
00394                 // fill in the CoAP response payload
00395                 coap_response->payload_ptr = NULL;
00396                 uint32_t payload_len = 0;
00397 
00398                 //If handler exists it means that resource value is stored in application side
00399                 if (block_message() && block_message()->is_block_message()) {
00400                     if(_outgoing_block_message_cb) {
00401                         String name = "";
00402                         if (received_coap_header->uri_path_ptr != NULL &&
00403                                 received_coap_header->uri_path_len > 0) {
00404                             name.append_raw((char *)received_coap_header->uri_path_ptr,
00405                                              received_coap_header->uri_path_len);
00406                         }
00407                         (*_outgoing_block_message_cb)(name, coap_response->payload_ptr, payload_len);
00408                     }
00409                 } else {
00410                     get_value(coap_response->payload_ptr,payload_len);
00411                 }
00412 
00413                 coap_response->payload_len = payload_len;
00414                 coap_response->options_list_ptr = sn_nsdl_alloc_options_list(nsdl, coap_response);
00415 
00416                 coap_response->options_list_ptr->max_age = max_age();
00417 
00418                 if(received_coap_header->options_list_ptr) {
00419                     if(received_coap_header->options_list_ptr->observe != -1) {
00420                         if (is_observable()) {
00421                             uint32_t number = 0;
00422                             uint8_t observe_option = 0;
00423                             observe_option = received_coap_header->options_list_ptr->observe;
00424 
00425                             if(START_OBSERVATION == observe_option) {
00426                                 tr_debug("M2MResourceInstance::handle_get_request - Starts Observation");
00427                                 // If the observe length is 0 means register for observation.
00428                                 if(received_coap_header->options_list_ptr->observe != -1) {
00429                                     number = received_coap_header->options_list_ptr->observe;
00430                                 }
00431                                 if(received_coap_header->token_ptr) {
00432                                     tr_debug("M2MResourceInstance::handle_get_request - Sets Observation Token to resource");
00433                                     set_observation_token(received_coap_header->token_ptr,
00434                                                           received_coap_header->token_len);
00435                                 }
00436                                 // If the observe value is 0 means register for observation.
00437                                 if(number == 0) {
00438                                     tr_debug("M2MResourceInstance::handle_get_request - Put Resource under Observation");
00439                                     set_under_observation(true,observation_handler);
00440                                     M2MBase::add_observation_level(M2MBase::R_Attribute);
00441                                     coap_response->options_list_ptr->observe = observation_number();
00442                                 }
00443                             } else if (STOP_OBSERVATION == observe_option) {
00444                                 tr_debug("M2MResourceInstance::handle_get_request - Stops Observation");
00445                                 set_under_observation(false,NULL);
00446                                 M2MBase::remove_observation_level(M2MBase::R_Attribute);
00447                             }
00448                         } else {
00449                             msg_code = COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED;
00450                         }
00451                     }
00452                 }
00453             }
00454         }else {
00455             tr_error("M2MResourceInstance::handle_get_request - Return COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED");
00456             // Operation is not allowed.
00457             msg_code = COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED;
00458         }
00459     } else {
00460         msg_code = COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED;
00461     }
00462     if(coap_response) {
00463         coap_response->msg_code = msg_code;
00464     }
00465     return coap_response;
00466 }
00467 
00468 sn_coap_hdr_s* M2MResourceInstance::handle_put_request(nsdl_s *nsdl,
00469                                                sn_coap_hdr_s *received_coap_header,
00470                                                M2MObservationHandler *observation_handler,
00471                                                bool &execute_value_updated)
00472 {
00473     tr_debug("M2MResourceInstance::handle_put_request()");
00474 
00475 
00476         sn_coap_msg_code_e msg_code = COAP_MSG_CODE_RESPONSE_CHANGED; // 2.04
00477         sn_coap_hdr_s *coap_response = sn_nsdl_build_response(nsdl,
00478                                                                received_coap_header,
00479                                                                msg_code);
00480         // process the PUT if we have registered a callback for it
00481         if(received_coap_header && coap_response) {
00482             uint16_t coap_content_type = 0;
00483             if(received_coap_header->content_format != COAP_CT_NONE) {
00484                 coap_content_type = received_coap_header->content_format;
00485             }
00486             if(received_coap_header->options_list_ptr &&
00487                received_coap_header->options_list_ptr->uri_query_ptr) {
00488                 char *query = (char*)alloc_string_copy(received_coap_header->options_list_ptr->uri_query_ptr,
00489                                                         received_coap_header->options_list_ptr->uri_query_len);
00490                 if (query){
00491                     tr_debug("M2MResourceInstance::handle_put_request() - Query %s", query);
00492 
00493                     // if anything was updated, re-initialize the stored notification attributes
00494                     if (!handle_observation_attribute(query)){
00495                         tr_debug("M2MResourceInstance::handle_put_request() - Invalid query");
00496                         msg_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; // 4.00
00497                     }
00498                     free(query);
00499                 }
00500             } else if ((operation() & SN_GRS_PUT_ALLOWED) != 0) {
00501                 tr_debug("M2MResourceInstance::handle_put_request() - Request Content-Type %d", coap_content_type);
00502 
00503                 if(COAP_CONTENT_OMA_TLV_TYPE == coap_content_type) {
00504                     msg_code = COAP_MSG_CODE_RESPONSE_UNSUPPORTED_CONTENT_FORMAT;
00505                 } else {
00506                     bool external_block_store = false;
00507                     if (block_message()) {
00508                         block_message()->set_message_info(received_coap_header);
00509                         if (block_message()->is_block_message()) {
00510                             external_block_store = true;
00511                             if(_incoming_block_message_cb) {
00512                                 (*_incoming_block_message_cb)(_block_message_data);
00513                             }
00514                             if (block_message()->is_last_block()) {
00515                                 block_message()->clear_values();
00516                                 coap_response->coap_status = COAP_STATUS_PARSER_BLOCKWISE_MSG_RECEIVED;
00517                             } else {
00518                                 coap_response->coap_status = COAP_STATUS_PARSER_BLOCKWISE_MSG_RECEIVING;
00519                             }
00520                             if (block_message()->error_code() != M2MBlockMessage::ErrorNone) {
00521                                 block_message()->clear_values();
00522                             }
00523                         }
00524                     }
00525                     if (!external_block_store) {
00526                         set_value(received_coap_header->payload_ptr, received_coap_header->payload_len);
00527                     }
00528                     if(received_coap_header->payload_ptr) {
00529                        tr_debug("M2MResourceInstance::handle_put_request() - Update Resource with new values");
00530                         if(observation_handler) {
00531                             String value = "";
00532                             if (received_coap_header->uri_path_ptr != NULL &&
00533                                 received_coap_header->uri_path_len > 0) {
00534                                 value.append_raw((char*)received_coap_header->uri_path_ptr, received_coap_header->uri_path_len);
00535                             }
00536                             execute_value_updated = true;
00537                         }
00538                     }
00539                 }
00540             } else {
00541                 // Operation is not allowed.
00542                 tr_error("M2MResourceInstance::handle_put_request() - COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED");
00543                 msg_code = COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED;
00544             }
00545         } else {
00546             msg_code = COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED;
00547         }
00548         if(coap_response) {
00549             coap_response->msg_code = msg_code;
00550         }
00551 
00552     return coap_response;
00553 }
00554 
00555 void M2MResourceInstance::set_resource_observer(M2MResourceCallback *resource)
00556 {
00557     _resource_callback = resource;
00558 }
00559 
00560 uint16_t M2MResourceInstance::object_instance_id() const
00561 {
00562     return _object_instance_id;
00563 }
00564 
00565 M2MBlockMessage* M2MResourceInstance::block_message() const
00566 {
00567     return _block_message_data;
00568 }
00569 
00570 void M2MResourceInstance::set_incoming_block_message_callback(incoming_block_message_callback callback)
00571 {
00572     // copy the callback object. This will change on next version to be a direct pointer to a interface class,
00573     // this FPn<> is just too heavy for this usage.
00574     delete _incoming_block_message_cb;
00575     _incoming_block_message_cb = new incoming_block_message_callback(callback);
00576 
00577     delete _block_message_data;
00578     _block_message_data = NULL;
00579     _block_message_data = new M2MBlockMessage();
00580 }
00581 
00582 void M2MResourceInstance::set_outgoing_block_message_callback(outgoing_block_message_callback callback)
00583 {
00584     delete _outgoing_block_message_cb;
00585     _outgoing_block_message_cb = new outgoing_block_message_callback(callback);
00586 }
00587 
00588 void M2MResourceInstance::set_notification_sent_callback(notification_sent_callback callback)
00589 {
00590     delete _notification_sent_callback;
00591     _notification_sent_callback = new notification_sent_callback(callback);
00592 }
00593 
00594 void M2MResourceInstance::set_notification_sent_callback(notification_sent_callback_2 callback)
00595 {
00596     delete _notification_sent_function_pointer;
00597 
00598     _notification_sent_function_pointer = new FP0<void>(callback);
00599     set_notification_sent_callback(
00600                 notification_sent_callback(_notification_sent_function_pointer, &FP0<void>::call));
00601 }
00602 
00603 void M2MResourceInstance::notification_sent()
00604 {
00605     if (_notification_sent_callback) {
00606         (*_notification_sent_callback)();
00607     }
00608 }
00609 
00610 M2MResource& M2MResourceInstance::get_parent_resource() const
00611 {
00612     return _parent_resource;
00613 }
00614 
00615 const char* M2MResourceInstance::object_name() const
00616 {
00617     const M2MObjectInstance& parent_object_instance = _parent_resource.get_parent_object_instance();
00618     const M2MObject& parent_object = parent_object_instance.get_parent_object();
00619 
00620     return parent_object.name();
00621 }