LCOV - code coverage report
Current view: top level - oks/src - object.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 27.0 % 1732 467
Test Date: 2026-07-12 15:23:06 Functions: 43.3 % 90 39

            Line data    Source code
       1              : // DUNE DAQ modification notice:
       2              : // This file has been modified from the original ATLAS oks source for the DUNE DAQ project.
       3              : // Fork baseline commit: oks-08-03-04 (2022-04-14).
       4              : // Renamed since fork: no.
       5              : 
       6              : #define _OksBuildDll_
       7              : 
       8              : #include "oks/object.hpp"
       9              : #include "oks/xml.hpp"
      10              : #include "oks/attribute.hpp"
      11              : #include "oks/relationship.hpp"
      12              : #include "oks/class.hpp"
      13              : #include "oks/kernel.hpp"
      14              : #include "oks/index.hpp"
      15              : #include "oks/profiler.hpp"
      16              : #include "oks/cstring.hpp"
      17              : 
      18              : #include "oks_utils.hpp"
      19              : 
      20              : #include <stdlib.h>
      21              : #include <stdio.h>
      22              : #include <string.h>
      23              : 
      24              : #include <iostream>
      25              : #include <sstream>
      26              : #include <stdexcept>
      27              : 
      28              : #include "ers/ers.hpp"
      29              : #include "logging/Logging.hpp"
      30              : 
      31              : namespace dunedaq {
      32              : namespace oks {
      33              : 
      34              :   // names of xml tags and attributes (differed for compact and extended files)
      35              : 
      36              : const char OksObject::obj_xml_tag []          = "obj";
      37              : const char OksObject::obj2_xml_tag []         = "o";
      38              : const char OksObject::class_xml_attribute[]   = "class";
      39              : const char OksObject::class2_xml_attribute[]  = "c";
      40              : const char OksObject::id_xml_attribute[]      = "id";
      41              : const char OksObject::id2_xml_attribute[]     = "i";
      42              : 
      43              : 
      44              :   // names of xml tags and attributes (update oks::cmp_strN calls, if touch below lines)
      45              : 
      46              : const char OksObject::attribute_xml_tag[]     = "attr";
      47              : const char OksObject::relationship_xml_tag[]  = "rel";
      48              : const char OksObject::name_xml_attribute[]    = "name";
      49              : const char OksObject::type_xml_attribute[]    = "type";
      50              : const char OksObject::num_xml_attribute[]     = "num";
      51              : const char OksObject::value_xml_attribute[]   = "val";
      52              : 
      53              : const char OksObject::data_xml_tag[]          = "data";
      54              : const char OksObject::ref_xml_tag[]           = "ref";
      55              : 
      56              : 
      57              :   std::string
      58            0 :   FailedReadObject::fill(const OksObject * o, const std::string& what, const std::string& reason)
      59              :   {
      60            0 :     std::ostringstream s;
      61            0 :     s << "Failed to read \'" << what << "\' of object " << o << '\n' << reason;
      62            0 :     return s.str();
      63            0 :   }
      64              : 
      65              :   std::string
      66            0 :   FailedCreateObject::fill(const OksObject * o, const std::string& reason)
      67              :   {
      68            0 :     std::ostringstream s;
      69            0 :     s << "Failed to create object " << o << '\n' << reason;
      70            0 :     return s.str();
      71            0 :   }
      72              : 
      73              :   std::string
      74            0 :   FailedSaveObject::fill(const OksObject * o, const std::string& reason)
      75              :   {
      76            0 :     std::ostringstream s;
      77            0 :     s << "Failed to save object " << o << '\n' << reason;
      78            0 :     return s.str();
      79            0 :   }
      80              : 
      81              :   std::string
      82            0 :   FailedRenameObject::fill(const OksObject * o, const std::string& reason)
      83              :   {
      84            0 :     std::ostringstream s;
      85            0 :     s << "Failed to rename object " << o << '\n' << reason;
      86            0 :     return s.str();
      87            0 :   }
      88              : 
      89              :   std::string
      90            0 :   FailedDestoyObject::fill(const OksObject * o, const std::string& reason)
      91              :   {
      92            0 :     std::ostringstream s;
      93            0 :     s << "Failed to destroy object " << o << '\n' << reason;
      94            0 :     return s.str();
      95            0 :   }
      96              : 
      97              :   std::string
      98            0 :   ObjectSetError::fill(const OksObject * obj, bool is_rel, const std::string& name, const std::string& reason)
      99              :   {
     100            0 :     std::ostringstream s;
     101            0 :     s << "Failed to set " << (is_rel ? "relationship" : "attribute") << " \'" << name << "\' of object " << obj << '\n' << reason;
     102            0 :     return s.str();
     103            0 :   }
     104              : 
     105              :   std::string
     106            0 :   ObjectGetError::fill(const OksObject * obj, bool is_rel, const std::string& name, const std::string& reason)
     107              :   {
     108            0 :     std::ostringstream s;
     109            0 :     s << "Failed to get " << (is_rel ? "relationship" : "attribute") << " \'" << name << "\' of object " << obj << '\n' << reason;
     110            0 :     return s.str();
     111            0 :   }
     112              : 
     113              :   std::string
     114            0 :   AddRcrError::fill(const OksObject * obj, const std::string& name, const OksObject * p1, const OksObject * p2)
     115              :   {
     116            0 :     std::ostringstream s;
     117            0 :     s << "Object " << obj << " cannot be linked with " << p1 << " via exclusive relationship \'" << name << "\' since it is already referenced by object " << p2;
     118            0 :     return s.str();
     119            0 :   }
     120              : 
     121              :   std::string
     122            0 :   ObjectInitError::fill(const OksObject * obj, const std::string& why, const std::string& reason)
     123              :   {
     124            0 :     std::ostringstream s;
     125            0 :     s << "Failed to init object " << obj << " because " << why << '\n' << reason;
     126            0 :     return s.str();
     127            0 :   }
     128              : 
     129              :   std::string
     130           18 :   ObjectBindError::fill(const OksObject * obj, const OksRelationship * rel, const std::string& why, const std::string& reason)
     131              :   {
     132           18 :     p_rel = rel;
     133           18 :     std::ostringstream s;
     134           18 :     s << "Failed to bind relationship";
     135           18 :     if(p_rel) { s << " \"" << p_rel->get_name() << '\"'; } else { s << 's'; }
     136           18 :     s << " of object " << obj << " because:\n" << why;
     137           18 :     if(!reason.empty()) s << '\n' << reason;
     138           36 :     return s.str();
     139           18 :   }
     140              : 
     141              : 
     142              : 
     143              : void
     144            0 : OksObject::set_unique_id()
     145              : { 
     146            0 :   OSK_PROFILING(OksProfiler::ObjectGetFreeObjectId, uid.class_id->get_kernel())
     147              : 
     148            0 :   p_duplicated_object_id_idx = uid.object_id.size();
     149              : 
     150            0 :   std::lock_guard lock(uid.class_id->p_unique_id_mutex);
     151              : 
     152            0 :   std::unique_ptr<char[]> unique_id(new char[p_duplicated_object_id_idx+16]);
     153              : 
     154            0 :   for (unsigned long i = 0; i < 0xFFFFFFFF; ++i)
     155              :     {
     156            0 :       sprintf(unique_id.get(), "%s^%lu", uid.object_id.c_str(), i);
     157            0 :       std::string id(unique_id.get());
     158            0 :       if(uid.class_id->get_object(id) == nullptr)
     159              :         {
     160            0 :           uid.object_id = id;
     161            0 :           return;
     162              :         }
     163            0 :     }
     164            0 : }
     165              : 
     166              : void
     167           27 : OksObject::check_ids()
     168              : {
     169           27 :   if (std::vector<OksClass *> * cs = uid.class_id->p_inheritance_hierarchy)
     170              :     {
     171          306 :       for (std::vector<OksClass *>::const_iterator j = cs->begin(); j != cs->end(); ++j)
     172              :         {
     173          279 :           OksObject * o = (*j)->get_object(uid.object_id);
     174          279 :           if (__builtin_expect((o != nullptr), 0))
     175              :             {
     176            0 :               std::stringstream e;
     177            0 :               e << "the object " << o << " from file \'" << o->get_file()->get_full_file_name() << "\' "
     178            0 :                   "has ID equal with \'" << uid.object_id << '@' << uid.class_id->get_name() << "\' object and their classes belong to the same inheritance hierarchy "
     179            0 :                   "(the OKS kernel is configured in the \"test-duplicated-objects-within-inheritance-hierarchy\" mode for compatibility with config and DAL tools)";
     180            0 :               throw oks::FailedCreateObject(this, e.str());
     181            0 :             }
     182              :         }
     183              :     }
     184           27 : }
     185              : 
     186              : void
     187         4973 : OksObject::init1(const OksFile * fp)
     188              : {
     189         4973 :   const OksClass * c = uid.class_id;
     190         4973 :   const std::string & object_id = uid.object_id;
     191         4973 :   const OksKernel * kernel(c->get_kernel());
     192              : 
     193              : 
     194              :     // do not allow to create an object of ABSTRACT class
     195              : 
     196         4973 :   if( __builtin_expect((c->get_is_abstract()), 0) ) {
     197            0 :     std::ostringstream text;
     198            0 :     text << "the class \'" << c->get_name() << "\' is abstract";
     199            0 :     throw oks::FailedCreateObject(this, text.str());
     200            0 :   }
     201              : 
     202              : 
     203              :     // do not allow two objects with equal ID in the same class
     204              : 
     205         4973 :   if( __builtin_expect((!object_id.empty()), 1) ) {
     206         4973 :     if (kernel->get_test_duplicated_objects_via_inheritance_mode() && !kernel->get_allow_duplicated_objects_mode()) {
     207           27 :       check_ids();
     208              :     }
     209              : 
     210         4973 :     OksObject * o = c->get_object(object_id);
     211              : 
     212         4975 :     if( __builtin_expect((o != nullptr), 0) ) {
     213            0 :       if(kernel->get_allow_duplicated_objects_mode()) {
     214            0 :         set_unique_id();
     215            0 :         if(kernel->get_silence_mode() != true) {
     216            0 :           std::cerr
     217              :             << "WARNING [OksObject constructor]:\n"
     218            0 :                "  Cannot create object " << o;
     219            0 :           if(fp) {
     220            0 :             std::cerr << " while reading file \'" << fp->get_full_file_name() << '\'';
     221              :           }
     222            0 :           std::cerr
     223              :             << "\n"
     224              :                "  Skip above problem since OKS kernel is configured in \'ignore-duplicated-objects\' mode\n"
     225            0 :             << "  Create duplicated object with ID = \'" << uid.object_id << "\'\n";
     226              :         }
     227              :       }
     228              :       else {
     229            0 :         std::stringstream e;
     230            0 :         e << "the object was already loaded from file \'" << o->get_file()->get_full_file_name() << '\'';
     231            0 :         throw oks::FailedCreateObject(this, e.str());
     232            0 :       }
     233              :     }
     234              :   }
     235              :   else {
     236            0 :       set_unique_id();
     237              :   }
     238              : 
     239        24573 :   data = new OksData[c->p_instance_size];
     240         4974 :   p_user_data = nullptr;
     241         4974 :   p_int32_id = 0;
     242         4974 :   p_rcr = nullptr;
     243         4974 :   file = kernel->get_active_data();
     244         4974 : }
     245              : 
     246              : void
     247        22128 : OksData::set_init_value(const OksAttribute * a, bool skip_init)
     248              : {
     249        22128 :   bool si = (a->get_init_value().empty() || skip_init);
     250              : 
     251        22128 :   if(a->get_is_multi_values() == false) {
     252        20352 :     type = a->get_data_type();
     253        20352 :     if(si) {
     254        12756 :       SetNullValue(a);
     255              :     }
     256              :     else {
     257         7596 :       if(type == OksData::string_type) {
     258         1479 :         data.STRING = new OksString(a->get_init_value());
     259              :       }
     260         6117 :       else if(type == OksData::enum_type) {
     261         1535 :         data.ENUMERATION = a->get_enum_value(a->get_init_value());
     262              :       }
     263              :       else {
     264         4582 :         SetValue(a->get_init_value().c_str(), a);
     265              :       }
     266              :     }
     267              :   }
     268              :   else {
     269         1776 :     if(si) {
     270         1668 :       SetValues("", a);
     271              :     }
     272              :     else {
     273          108 :       SetValues(a->get_init_value().c_str(), a);
     274              :     }
     275              :   }
     276        22128 : }
     277              : 
     278              : 
     279              : void
     280         4973 : OksObject::init2(bool skip_init)
     281              : {
     282         4973 :   int count = 0;
     283         4973 :   const OksClass * c = uid.class_id;
     284              : 
     285         4973 :   if (c->p_all_attributes)
     286              :     {
     287         4973 :       if (skip_init == false)
     288              :         {
     289        17740 :           for (const auto& i : *c->p_all_attributes)
     290        12768 :             data[count++] = i->p_init_data;
     291              :         }
     292              :       else
     293              :         {
     294            0 :           for (const auto& i : *c->p_all_attributes)
     295            0 :             data[count++] = i->p_empty_init_data;
     296              :         }
     297              :     }
     298              : 
     299         4974 :   if(c->p_all_relationships) {
     300        11802 :     for(const auto& i : *c->p_all_relationships) {
     301         6828 :       OksData& d(data[count++]);
     302         6828 :       if(i->get_high_cardinality_constraint() != OksRelationship::Many) {
     303         2897 :         d.type = OksData::object_type;
     304         2897 :         d.data.OBJECT = nullptr;
     305              :       }
     306              :       else {
     307         3931 :         d.type = OksData::list_type;
     308         3931 :         d.data.LIST = new OksData::List();
     309              :       }
     310              :     }
     311              :   }
     312         4975 : }
     313              : 
     314              : void
     315         4973 : OksObject::init3(OksClass * c)
     316              : {
     317         4973 :   if( __builtin_expect((c->p_indices != nullptr), 0) ) {
     318            0 :     for(auto& i : *c->p_indices) i.second->insert(this);
     319              :   }
     320              : 
     321         4973 :   c->add(this);
     322         4974 :   c->p_kernel->define(this);
     323         4975 : }
     324              : 
     325              : void
     326           27 : OksObject::init3()
     327              : {
     328           27 :   init3(const_cast<OksClass *>(uid.class_id));
     329           27 :   create_notify();
     330           27 : }
     331              : 
     332              : #define READ_OBJ_HEADER(F1, S1, F2, S2)                                                 \
     333              :   while(true) {                                                                         \
     334              :     OksXmlAttribute attr(read_params.s);                                                \
     335              :     if(oks::cmp_str1(attr.name(), ">") || oks::cmp_str1(attr.name(), "/")) { break; }   \
     336              :     else if(F1(attr.name(), S1)) {                                                      \
     337              :       std::shared_lock lock(read_params.oks_kernel->p_schema_mutex);                    \
     338              :       c = read_params.oks_kernel->find_class(attr.value());                             \
     339              :       if( __builtin_expect((c == nullptr), 0) ) {                                       \
     340              :         std::ostringstream text;                                                        \
     341              :         text << "cannot find class \"" << attr.value() << "\" (line "                   \
     342              :              << read_params.s.get_line_no() << ", char "                                \
     343              :              << read_params.s.get_line_pos() << ')';                                    \
     344              :         throw std::runtime_error( text.str().c_str() );                                 \
     345              :       }                                                                                 \
     346              :     }                                                                                   \
     347              :     else if(F2(attr.name(), S2)) id.assign(attr.value(), attr.value_len());             \
     348              :     else {                                                                              \
     349              :       read_params.s.throw_unexpected_attribute(attr.name());                            \
     350              :     }                                                                                   \
     351              :   }
     352              : 
     353              : 
     354              : void
     355          125 : oks::ReadFileParams::init()
     356              : {
     357          125 :   if(format != 'c') {
     358          125 :     object_tag = OksObject::obj_xml_tag;
     359          125 :     object_tag_len = sizeof(OksObject::obj_xml_tag);
     360              :   }
     361              :   else {
     362            0 :     object_tag = OksObject::obj2_xml_tag;
     363            0 :     object_tag_len = sizeof(OksObject::obj2_xml_tag);
     364              :   }
     365          125 : }
     366              : 
     367              : OksObject *
     368         5073 : OksObject::read(const oks::ReadFileParams& read_params)
     369              : {
     370              :     // read 'object' tag header
     371              : 
     372         5073 :   try {
     373         5073 :     const char * tag_start(read_params.s.get_tag_start());
     374              : 
     375         5073 :     if(memcmp(tag_start, read_params.object_tag, read_params.object_tag_len)) {
     376          125 :       if(!oks::cmp_str9(tag_start, "/oks-data")) {
     377            0 :         read_params.s.throw_unexpected_tag(tag_start, read_params.object_tag);
     378              :       }
     379          125 :       return 0;
     380              :     }
     381              :   }
     382            0 :   catch (oks::exception & e) {
     383            0 :     throw oks::FailedRead("start-of-object tag", e);
     384            0 :   }
     385            0 :   catch (std::exception & e) {
     386            0 :     throw oks::FailedRead("start-of-object tag", e.what());
     387            0 :   }
     388              : 
     389              : 
     390              :     // read object tag attributes
     391              : 
     392         4948 :   OksClass * c(nullptr);
     393         4948 :   std::string& id((const_cast<oks::ReadFileParams&>(read_params)).tmp);  // use temporal string for fast assignment
     394              : 
     395              : 
     396         4948 :   try {
     397         4948 :     if(read_params.format != 'c') {
     398        14844 :       READ_OBJ_HEADER(oks::cmp_str5, class_xml_attribute, oks::cmp_str2, id_xml_attribute)
     399              :     }
     400              :     else {
     401            0 :       READ_OBJ_HEADER(oks::cmp_str1, class2_xml_attribute, oks::cmp_str1, id2_xml_attribute)
     402              :     }
     403              :   }
     404            0 :   catch(oks::exception & e) {
     405            0 :     throw oks::FailedRead("object header", e);
     406            0 :   }
     407            0 :   catch (std::exception & e) {
     408            0 :     throw oks::FailedRead("object header", e.what());
     409            0 :   }
     410              : 
     411         4948 :   if(!c) {
     412            0 :     throw oks::BadFileData("object without class", read_params.s.get_line_no(), read_params.s.get_line_pos());
     413              :   }
     414              : 
     415         4948 :   try {
     416         4948 :     oks::validate_not_empty(id, "object id");
     417              :   }
     418            0 :   catch(std::exception& ex) {
     419            0 :     throw oks::FailedRead("oks object", oks::BadFileData(ex.what(), read_params.s.get_line_no(), read_params.s.get_line_pos()));
     420            0 :   }
     421              : 
     422         4948 :   if(c && read_params.reload_objects) {
     423            0 :     if(OksObject * o = read_params.reload_objects->pop(c, id)) {
     424            0 :       o->read_body(read_params, true);
     425            0 :       return o;
     426              :     }
     427              :   }
     428              : 
     429              : 
     430              :     // check if this is the end of file
     431              : 
     432         4948 :   if(c == nullptr && id.empty()) { return nullptr; }
     433              : 
     434              : 
     435              :     // create new object
     436              : 
     437         4948 :   OksObject * obj = new OksObject(read_params, c, id);  // can throw exception
     438              : 
     439              : 
     440              :     // check if we re-read the database and notify if required
     441              : 
     442         4948 :   if(read_params.reload_objects) {
     443            0 :     read_params.reload_objects->created.push_back(obj);
     444            0 :     obj->create_notify();
     445              : 
     446            0 :     if(read_params.oks_kernel->get_verbose_mode()) {
     447            0 :       TLOG_DEBUG(3) << "*** create object " << obj << " while reload data ***";
     448              :     }
     449              :   }
     450              : 
     451              :   return obj;
     452              : }
     453              : 
     454              : 
     455              : void
     456            0 : OksObject::__report_type_cvt_warning(const oks::ReadFileParams& params, const OksAttribute& a, const OksData&d, OksData::Type type, int32_t num) const
     457              : {
     458            0 :   const char * type_str;
     459              : 
     460            0 :   switch (type) {
     461            0 :     case OksData::s8_int_type:     type_str = OksAttribute::s8_int_type  ; break;
     462            0 :     case OksData::u8_int_type:     type_str = OksAttribute::u8_int_type  ; break;
     463            0 :     case OksData::s16_int_type:    type_str = OksAttribute::s16_int_type ; break;
     464            0 :     case OksData::u16_int_type:    type_str = OksAttribute::u16_int_type ; break;
     465            0 :     case OksData::s32_int_type:    type_str = OksAttribute::s32_int_type ; break;
     466            0 :     case OksData::u32_int_type:    type_str = OksAttribute::u32_int_type ; break;
     467            0 :     case OksData::s64_int_type:    type_str = OksAttribute::s64_int_type ; break;
     468            0 :     case OksData::u64_int_type:    type_str = OksAttribute::u64_int_type ; break;
     469            0 :     case OksData::float_type:      type_str = OksAttribute::float_type   ; break;
     470            0 :     case OksData::double_type:     type_str = OksAttribute::double_type  ; break;
     471            0 :     case OksData::bool_type:       type_str = OksAttribute::bool_type    ; break;
     472            0 :     case OksData::class_type:      type_str = OksAttribute::class_type   ; break;
     473            0 :     case OksData::date_type:       type_str = OksAttribute::date_type    ; break;
     474            0 :     case OksData::time_type:       type_str = OksAttribute::time_type    ; break;
     475            0 :     case OksData::string_type:     type_str = OksAttribute::string_type  ; break;
     476            0 :     case OksData::enum_type:       type_str = OksAttribute::enum_type    ; break;
     477              :     default:                       type_str = "unknown";
     478              :     
     479              :   }
     480              : 
     481            0 :   std::lock_guard lock(OksKernel::p_parallel_out_mutex);
     482            0 :   Oks::warning_msg(std::string("Read object \"") + GetId() + '@' + GetClass()->get_name() + '\"')
     483            0 :     << "  file: \"" << params.f->get_full_file_name() << "\" (line: " << params.s.get_line_no() << ", pos: " << params.s.get_line_pos() << ")\n"
     484            0 :        "  convert value from wrong type \"" << (num == -1 ? "single" : "multi") << "-value " << type_str << "\" to \"" << (a.get_is_multi_values() ? "multi" : "single")
     485            0 :     << "-value " << a.get_type() << "\" as required for attribute \"" << a.get_name() << "\"\n"
     486            0 :     << "  converted value: " << d << std::endl;
     487            0 : }
     488              : 
     489              : void
     490            0 : OksObject::__report_cardinality_cvt_warning(const oks::ReadFileParams& params, const OksRelationship& r) const
     491              : {
     492            0 :   std::lock_guard lock(OksKernel::p_parallel_out_mutex);
     493            0 :   Oks::warning_msg(std::string("Read object \"") + GetId() + '@' + GetClass()->get_name() + '\"')
     494            0 :     << "  file: \"" << params.f->get_full_file_name() << "\" (line: " << params.s.get_line_no() << ", pos: " << params.s.get_line_pos() << ")\n"
     495            0 :        "  mismatch between relationship \"" << r.get_name() << "\" cardinality and its value (save data file with new schema)." << std::endl;
     496            0 : }
     497              : 
     498              : static void
     499            0 : __throw_unknown_type(const oks::ReadFileParams& params, const char * atype)
     500              : {
     501            0 :   std::ostringstream s;
     502            0 :   s << "Unknown attribute type \"" << atype << '\"';
     503            0 :   throw oks::BadFileData(s.str(), params.s.get_line_no(), params.s.get_line_pos());
     504            0 : }
     505              : 
     506              : int32_t
     507            0 : OksObject::__get_num(const oks::ReadFileParams& params)
     508              : {
     509            0 :   char * __sanity;
     510            0 :   params.s.get_num_token('\"');
     511            0 :   int32_t num = static_cast<int32_t>(strtol(params.s.m_cvt_char->m_buf, &__sanity, 0));
     512            0 :   if(*__sanity != 0 || errno == ERANGE) OksXmlInputStream::__throw_strto("strtol", __sanity, params.s.m_cvt_char->m_buf, params.s.get_line_no(), params.s.get_line_pos());
     513            0 :   return num;
     514              : }
     515              : 
     516              : 
     517              : void
     518         4947 : OksObject::read_body(const oks::ReadFileParams& read_params, bool re_read)
     519              : {
     520         4947 :   std::string& i_name((const_cast<oks::ReadFileParams&>(read_params)).tmp);
     521         4947 :   bool non_silent_mode(!read_params.oks_kernel->get_silence_mode());  // detect if we need to print errors
     522              : 
     523         4947 :   OksData dd;                                         // temporal data
     524         4946 :   const OksClass * c = uid.class_id;                  // pointer to object's class
     525              : 
     526         4946 :   bool was_updated = false;                           // is used when object is re-read from file
     527         4946 :   bool check_re_read = (re_read && read_params.oks_kernel->p_change_object_notify_fn);
     528              : 
     529         4946 :   char * __sanity;                                    // used to check mu, token
     530              : 
     531              : 
     532              :     // set file from which this object was read
     533              : 
     534         4946 :   file = read_params.f;
     535              : 
     536              : 
     537              :     // is used by ReadFrom(relationship)
     538              : 
     539         4946 :   const_cast<oks::ReadFileParams&>(read_params).owner = this;
     540              : 
     541         4946 :   if( __builtin_expect((read_params.format != 'c'), 1) ) {
     542         4946 :     OksDataInfo::Map::iterator info_i;
     543         4946 :     OksDataInfo * info(0);
     544              : 
     545              : 
     546              :       // are used when object is re-read from file
     547              : 
     548         4946 :     std::list<const OksAttribute *> * read_attributes = nullptr;
     549         4946 :     std::list<const OksRelationship *> * read_relationships = nullptr;
     550              : 
     551         4946 :     if( __builtin_expect((check_re_read), 0) ) {
     552            0 :       if(c->p_all_attributes && !c->p_all_attributes->empty()) {
     553            0 :         read_attributes = new std::list<const OksAttribute *>();
     554            0 :         for(auto& i : *c->p_all_attributes)
     555            0 :           read_attributes->push_back(i);
     556              :       }
     557              : 
     558            0 :       if(c->p_all_relationships && !c->p_all_relationships->empty()) {
     559            0 :         read_relationships = new std::list<const OksRelationship *>();
     560            0 :         for(auto& i : *c->p_all_relationships)
     561            0 :           read_relationships->push_back(i);
     562              :       }
     563              :     }
     564         4946 :     else if( __builtin_expect((c != nullptr), 1) ) {
     565         4946 :       if( __builtin_expect((re_read == true), 0) ) {
     566            0 :         OksData *d_end = data + c->number_of_all_attributes() + c->number_of_all_relationships();
     567            0 :         for(OksData *di = data; di < d_end; ++di) {
     568            0 :           di->Clear();
     569              :         }
     570              :       }
     571              : 
     572         4946 :       init2();
     573              :     }
     574              : 
     575              : 
     576              :       // read object body
     577              : 
     578        18715 :     while(true) try {
     579              : 
     580        18715 :       const char * tag_start = read_params.s.get_tag_start();
     581              : 
     582              :         // check for closing tag
     583              : 
     584        18717 :       if(*tag_start == '/' && !memcmp(tag_start + 1, read_params.object_tag, read_params.object_tag_len)) { break; }
     585              : 
     586              : 
     587              :         // extra check, if the object is empty
     588              : 
     589        13771 :       if(oks::cmp_str3(tag_start, "obj")) {
     590            0 :         read_params.s.seek_position(-5);
     591              :         break;
     592              :       }
     593              : 
     594              : 
     595              :         // read attribute
     596              : 
     597        13771 :       else if(oks::cmp_str4(tag_start, attribute_xml_tag)) {
     598         9398 :         OksData::Type type(OksData::unknown_type);
     599         9398 :         int32_t num = -1; // -1 => no mv-data read
     600         9398 :         const OksAttribute * a(0);
     601         9398 :         OksXmlValue value;
     602         9398 :         bool attr_is_closed(false);
     603              : 
     604              :           // read 'oks-attribute' tag attributes
     605              : 
     606         9398 :         try {
     607              : 
     608         9398 :           bool name_was_read(false);
     609              : 
     610        65210 :           while(true) {
     611        37303 :             OksXmlAttribute attr(read_params.s);
     612              : 
     613              :             // check for close of tag
     614        37298 :             if(oks::cmp_str1(attr.name(), ">")) { break; }
     615              : 
     616        37013 :             if(read_params.format == 'n' && oks::cmp_str1(attr.name(), "/"))
     617              :               {
     618              :                 attr_is_closed = true;
     619              :                 break;
     620              :               }
     621              : 
     622              :             // check for known oks-attribute' attributes
     623        27906 :             if(oks::cmp_str4(attr.name(), name_xml_attribute)) {
     624         9396 :               name_was_read = true;
     625         9396 :               if( __builtin_expect((c != nullptr), 1) ) {
     626         9396 :                 i_name.assign(attr.value(), attr.value_len());
     627         9397 :                 info_i = c->p_data_info->find(i_name);
     628         9397 :                 if( __builtin_expect((info_i == c->p_data_info->end() || info_i->second->attribute == nullptr), 0) ) {
     629            9 :                   if(non_silent_mode) {
     630            0 :                     std::string msg = std::string("Read object \"") + GetId() + '@' + c->get_name() + '\"';
     631            0 :                     std::lock_guard lock(OksKernel::p_parallel_out_mutex);
     632            0 :                     Oks::warning_msg(msg.c_str()) << "  skip attribute \"" << attr.value() << "\", which is not defined in the schema\n";
     633            0 :                   }
     634              :                 }
     635              :                 else {
     636         9388 :                   info = info_i->second;
     637         9388 :                   a = info->attribute;
     638              :                 }
     639              :               }
     640              :             }
     641        18510 :             else if(oks::cmp_str4(attr.name(), type_xml_attribute)) {
     642         9397 :               type = OksAttribute::get_data_type(attr.value(),attr.value_len());
     643         9397 :               if( __builtin_expect((type == OksData::unknown_type), 0) ) {
     644            0 :                 if(
     645            0 :                   attr.value()[0] != 0 &&
     646              :                   (
     647            0 :                     (attr.value()[0] == '-' && attr.value_len() != 1) ||
     648              :                     (attr.value()[0] != '-')
     649              :                   )
     650              :                 ) {
     651            0 :                   __throw_unknown_type(read_params,attr.value());
     652              :                 }
     653              :               }
     654              :             }
     655         9113 :             else if(read_params.format != 'n' && oks::cmp_str3(attr.name(), num_xml_attribute)) {
     656            0 :               num = static_cast<int32_t>(strtol(attr.value(), &__sanity, 0));
     657            0 :               if( __builtin_expect((*__sanity != 0 || errno == ERANGE), 0) ) OksXmlInputStream::__throw_strto("strtoul", __sanity, attr.value(), read_params.s.get_line_no(), read_params.s.get_line_pos());
     658              :             }
     659         9113 :             else if(read_params.format == 'n' && oks::cmp_str3(attr.name(), value_xml_attribute)) {
     660         9113 :               value = read_params.s.get_value(attr.p_value_len);
     661              :             }
     662              :             else {
     663            0 :               read_params.s.throw_unexpected_attribute(attr.name());
     664              :             }
     665        27907 :           }
     666              : 
     667         9392 :           if( __builtin_expect((!name_was_read), 0) ) {
     668            0 :             throw oks::BadFileData("attribute without name", read_params.s.get_line_no(), read_params.s.get_line_pos());
     669              :           }
     670              : 
     671         9392 :           if (read_params.format == 'n' && value.is_empty() == true)
     672              :             num = 0;
     673              :         }
     674            0 :         catch(oks::exception & e) {
     675            0 :           throw oks::FailedRead("attribute value header", e);
     676            0 :         }
     677            0 :         catch (std::exception & e) {
     678            0 :           throw oks::FailedRead("attribute value header", e.what());
     679            0 :         }
     680              : 
     681              :         // read 'oks-attribute' body
     682         9392 :         if( __builtin_expect((a != nullptr), 1) ) {
     683         9383 :           OksData * d = &data[info->offset];
     684         9383 :           OksData * dx;
     685              : 
     686         9383 :           if( __builtin_expect((check_re_read), 0) ) {
     687            0 :             dx = &dd;
     688            0 :             read_attributes->remove(a);
     689              :           }
     690              :           else {
     691              :             dx = d;
     692              :           }
     693              : 
     694              : 
     695              :           // set default type if it was not read from file
     696         9383 :           if( __builtin_expect((type == OksData::unknown_type), 0) ) {
     697            0 :             type = a->get_data_type();
     698              :           }
     699              : 
     700              : 
     701         9383 :           try {
     702              : 
     703              :               // read value and convert if necessary
     704              : 
     705         9383 :             if(num == -1) {
     706         9098 :               if( __builtin_expect((a->get_data_type() != type || a->get_is_multi_values()), 0) ) {
     707           27 :                 OksAttribute a2((type != OksData::enum_type ? type : OksData::string_type), c);
     708              : 
     709           27 :                 OksData d2;
     710           27 :                 if(read_params.format != 'n')
     711            0 :                   d2.read(read_params, &a2);
     712              :                 else
     713           27 :                   d2.read(&a2, value);
     714           27 :                 d2.cvt(dx, a);
     715           27 :                 if(non_silent_mode) {
     716            0 :                   __report_type_cvt_warning(read_params, *a, *dx, type, num);
     717              :                 }
     718           27 :               }
     719              :               else {
     720         9071 :                 if(read_params.format != 'n')
     721            0 :                   dx->read(read_params, a);
     722              :                 else
     723         9071 :                   dx->read(a, value);
     724              :               }
     725              :             }
     726              :             else {
     727          285 :               if( __builtin_expect((a->get_data_type() != type || !a->get_is_multi_values()), 0) ) {
     728            0 :                 OksAttribute a2((type != OksData::enum_type ? type : OksData::string_type), c);
     729            0 :                 OksData d2;
     730            0 :                 if(read_params.format != 'n')
     731            0 :                   d2.read(read_params, &a2, num);
     732              :                 else
     733              :                   {
     734            0 :                     if(attr_is_closed == false)
     735            0 :                       d2.read(&a2, read_params);
     736              :                     else
     737            0 :                       d2.Set(new OksData::List());
     738              :                   }
     739            0 :                 d2.cvt(dx, a);
     740            0 :                 if(non_silent_mode) {
     741            0 :                   __report_type_cvt_warning(read_params, *a, *dx, type, num);
     742              :                 }
     743            0 :               }
     744              :               else {
     745          285 :                 if(read_params.format != 'n')
     746            0 :                   dx->read(read_params, a, num);
     747              :                 else
     748              :                   {
     749          285 :                   if(attr_is_closed == false)
     750          285 :                     dx->read(a, read_params);
     751              :                   else
     752            0 :                     dx->Set(new OksData::List());
     753              :                   }
     754              :               }
     755              :             }
     756              : 
     757              :               // check validity range
     758              : 
     759         9385 :             dx->check_range(a);
     760              :           }
     761            0 :           catch (oks::exception & e) {
     762            0 :             throw oks::FailedReadObject(this, std::string("attribute \"") + a->get_name() + '\"', e);
     763            0 :           }
     764            0 :           catch (std::exception & e) {
     765            0 :             throw oks::FailedReadObject(this, std::string("attribute \"") + a->get_name() + '\"', e.what());
     766            0 :           }
     767              : 
     768              : 
     769         9385 :           if( __builtin_expect((check_re_read), 0) ) {
     770            0 :             if(*dx != *d) {
     771            0 :               was_updated = true;
     772            0 :               *d = *dx;
     773              :             }
     774            0 :             dd.Clear();
     775              :           }
     776              :         }
     777              :         else {
     778            9 :           try {
     779           18 :             OksAttribute a2((type != OksData::enum_type ? type : OksData::string_type), c);
     780            9 :             if(num == -1) {
     781              :               // note, there is no need to read single-value in case of "normal" format
     782            9 :               if(read_params.format != 'n')
     783            0 :                 OksData(read_params, &a2);
     784              :             }
     785              :             else {
     786            0 :               if(read_params.format != 'n')
     787            0 :                 OksData(read_params, &a2, num);
     788              :               else
     789            0 :                 OksData(&a2, read_params);
     790              :             }
     791            9 :           }
     792            0 :           catch (oks::exception & e) {
     793            0 :             throw oks::FailedReadObject(this, "dummu attribute", e);
     794            0 :           }
     795            0 :           catch (std::exception & e) {
     796            0 :             throw oks::FailedReadObject(this, "dummu attribute", e.what());
     797            0 :           }
     798              :         }
     799              : 
     800              : 
     801              :           // read 'oks-attribute' close tag
     802              : 
     803         9394 :         if(read_params.format != 'n')
     804            0 :           try {
     805            0 :             const char * eoa_tag_start = read_params.s.get_tag_start();
     806              : 
     807            0 :             if(!oks::cmp_str5(eoa_tag_start, "/attr")) {
     808            0 :               std::string s("\'/attr\' is expected instead of \'");
     809            0 :               s += eoa_tag_start;
     810            0 :               s += '\'';
     811            0 :               throw std::runtime_error( s.c_str() );
     812            0 :             }
     813              :           }
     814            0 :           catch (oks::exception & e) {
     815            0 :             throw oks::FailedRead(std::string("attribute\'s closing tag"), e);
     816            0 :           }
     817            0 :           catch (std::exception & e) {
     818            0 :             throw oks::FailedRead(std::string("attribute\'s closing tag"), e.what());
     819            0 :           }
     820              : 
     821              :       }  // end of "read attribute"
     822              :  
     823              : 
     824              :         // read relationship
     825              : 
     826         4373 :       else if(oks::cmp_str3(tag_start, relationship_xml_tag)) {
     827         4373 :         int32_t num = -1; // -1 => no mv-data read
     828         4373 :         OksRelationship * r(nullptr);
     829         4373 :         OksXmlRelValue value(read_params);
     830              : 
     831              :           // read 'oks-relationship' tag attributes
     832              : 
     833        13880 :         try {
     834        13880 :           while(true) {
     835        13880 :             OksXmlAttribute attr(read_params.s);
     836              : 
     837              :               // check for close of tag
     838              : 
     839        13880 :             if(oks::cmp_str1(attr.name(), ">") || (read_params.format == 'n' && oks::cmp_str1(attr.name(), "/"))) { break; }
     840              : 
     841              :               // check for known oks-relationship' attributes
     842              : 
     843         9507 :             else if(oks::cmp_str4(attr.name(), name_xml_attribute)) {
     844         4373 :               if( __builtin_expect((c != nullptr), 1) ) {
     845         4373 :                 i_name.assign(attr.value(), attr.value_len());
     846         4373 :                 info_i = c->p_data_info->find(i_name);
     847         4373 :                 if( __builtin_expect((info_i == c->p_data_info->end() || info_i->second->relationship == nullptr), 0) ) {
     848            0 :                   if(non_silent_mode) {
     849            0 :                     std::string msg = std::string("Read object \"") + GetId() + '@' + c->get_name() + '\"';
     850            0 :                     std::lock_guard lock(OksKernel::p_parallel_out_mutex);
     851            0 :                     Oks::warning_msg(msg.c_str()) << "  skip relationship \"" << attr.value() << "\", which is not defined in the schema\n";
     852            0 :                   }
     853              :                 }
     854              :                 else {
     855         4373 :                   info = info_i->second;
     856         4373 :                   r = const_cast<OksRelationship *>(info->relationship);
     857              :                 }
     858              :               }
     859         9507 :               continue;
     860         4373 :             }
     861         5134 :             else if(read_params.format == 'n') {
     862         5134 :               if(oks::cmp_str5(attr.name(), class_xml_attribute)) {
     863         2567 :                 value.m_class = uid.class_id->get_kernel()->find_class(attr.value());
     864         2567 :                 if( __builtin_expect((value.m_class == nullptr && attr.value_len() > 0), 0) ) {
     865            0 :                   value.m_class_id = new OksString(attr.value(), attr.value_len());
     866              :                 }
     867         2567 :                 continue;
     868              :               }
     869         2567 :               else if(oks::cmp_str2(attr.name(), id_xml_attribute)) {
     870         2567 :                 value.m_value = read_params.s.get_value(attr.p_value_len);
     871         2567 :                 continue;
     872              :               }
     873              :             }
     874              :             else {
     875            0 :               if(oks::cmp_str3(attr.name(), num_xml_attribute)) {
     876            0 :                 num = static_cast<int32_t>(strtol(attr.value(), &__sanity, 0));
     877            0 :                 if( __builtin_expect((*__sanity != 0 || errno == ERANGE), 0) ) OksXmlInputStream::__throw_strto("strtoul", __sanity, attr.value(), read_params.s.get_line_no(), read_params.s.get_line_pos());
     878            0 :                 continue;
     879              :               }
     880              :             }
     881              : 
     882            0 :             read_params.s.throw_unexpected_attribute(attr.name());
     883              :           }
     884              :         }
     885            0 :         catch(oks::exception & e) {
     886            0 :           throw oks::FailedRead("relationship value header", e);
     887            0 :         }
     888            0 :         catch (std::exception & e) {
     889            0 :           throw oks::FailedRead("relationship value header", e.what());
     890            0 :         }
     891              : 
     892         4373 :         if (read_params.format == 'n' && value.is_empty() == true)
     893              :           num = 0;
     894              : 
     895              :           // read 'oks-relationship' body
     896              : 
     897         4373 :         if( __builtin_expect((r != nullptr), 1) ) {
     898         4373 :           OksData *d = &data[info->offset];
     899         4373 :           OksData * dx;
     900              : 
     901         4373 :           OksRelationship::CardinalityConstraint cc = r->get_high_cardinality_constraint();
     902              : 
     903         4373 :           if( __builtin_expect((check_re_read), 0) ) {
     904            0 :             dx = &dd;
     905            0 :             read_relationships->remove(r);
     906              :           }
     907              :           else {
     908              :             dx = d;
     909              :           }
     910              : 
     911         4373 :           try {
     912         4373 :             if(num == -1) {
     913         2567 :               if(cc == OksRelationship::Many) {
     914            0 :                 __report_cardinality_cvt_warning(read_params, *r);
     915            0 :                 OksRelationship dummy(*r);
     916            0 :                 OksData d2;
     917            0 :                 if(read_params.format == 'n')
     918            0 :                   d2.read(&dummy, value);
     919              :                 else
     920            0 :                   d2.read(read_params, &dummy);
     921            0 :                 d2.ConvertTo(dx, r);
     922            0 :               }
     923              :               else {
     924         2567 :                 if(read_params.format == 'n')
     925         2567 :                   dx->read(r, value);
     926              :                 else
     927            0 :                   dx->read(read_params, r);
     928              :               }
     929              :             }
     930              :             else {
     931         1806 :               if(cc != OksRelationship::Many) {
     932            0 :                 __report_cardinality_cvt_warning(read_params, *r);
     933            0 :                 OksRelationship dummy(*r);
     934            0 :                 OksData d2;
     935            0 :                 if(read_params.format == 'n')
     936            0 :                   d2.read(&dummy, read_params);
     937              :                 else
     938            0 :                   d2.read(read_params, &dummy, num);
     939            0 :                 d2.ConvertTo(dx, r);
     940            0 :               }
     941              :               else {
     942         1806 :                 if(read_params.format == 'n')
     943         1806 :                   dx->read(r, read_params);
     944              :                 else
     945            0 :                   dx->read(read_params, r, num);
     946              :               }
     947              :             }
     948              :           }
     949            0 :           catch (oks::exception & e) {
     950            0 :             throw oks::FailedReadObject(this, std::string("relationship \"") + r->get_name() + '\"', e);
     951            0 :           }
     952            0 :           catch (std::exception & e) {
     953            0 :             throw oks::FailedReadObject(this, std::string("relationship \"") + r->get_name() + '\"', e.what());
     954            0 :           }
     955              : 
     956         4373 :           if(check_re_read) {
     957            0 :             if(*dx != *d) {
     958            0 :               was_updated = true;
     959            0 :               *d = *dx;
     960              :             }
     961            0 :             dd.Clear();
     962              :           }
     963              :         }
     964              :         else {
     965            0 :           OksRelationship r("_ _ *** dummy *** _ _"); // temporal relationship to use right OksData constructor call
     966              : 
     967            0 :           try {
     968            0 :             if(num == -1) {
     969            0 :               if(read_params.format != 'n')
     970            0 :                 OksData(read_params, &r);
     971              :             }
     972              :             else {
     973            0 :               if(read_params.format == 'n')
     974            0 :                 OksData(&r, read_params);
     975              :               else
     976            0 :                 OksData(read_params, &r, num);
     977              :             }
     978              :           }
     979            0 :           catch (oks::exception & e) {
     980            0 :             throw oks::FailedReadObject(this, "dummu relationship", e);
     981            0 :           }
     982            0 :           catch (std::exception & e) {
     983            0 :             throw oks::FailedReadObject(this, "dummu relationship", e.what());
     984            0 :           }
     985            0 :         }
     986              : 
     987              : 
     988              :           // read 'oks-relationship' close tag
     989              : 
     990         4373 :         if(read_params.format != 'n')
     991            0 :           try {
     992            0 :             const char * eor_tag_start = read_params.s.get_tag_start();
     993              : 
     994            0 :             if(!oks::cmp_str4(eor_tag_start, "/rel")) {
     995            0 :               std::string s("\'/rel\' is expected instead of \'");
     996            0 :               s += eor_tag_start;
     997            0 :               s += '\'';
     998            0 :               throw std::runtime_error( s.c_str() );
     999            0 :             }
    1000              :           }
    1001            0 :           catch (oks::exception & e) {
    1002            0 :             throw oks::FailedRead(std::string("relationship\'s closing tag"), e);
    1003            0 :           }
    1004            0 :           catch (std::exception & e) {
    1005            0 :             throw oks::FailedRead(std::string("relationship\'s closing tag"), e.what());
    1006            0 :           }
    1007              : 
    1008              :       }  // end of "read relationship"
    1009              : 
    1010              :     }
    1011              : 
    1012            0 :     catch (oks::exception & e) {
    1013            0 :       throw oks::FailedRead(std::string("object \"") + uid.object_id + '@' + c->get_name() + '\"', e);
    1014            0 :     }
    1015            0 :     catch (std::exception & e) {
    1016            0 :       throw oks::FailedRead(std::string("object \"") + uid.object_id + '@' + c->get_name() + '\"', e.what());
    1017            0 :     }
    1018              : 
    1019              : 
    1020              :       // check that unread attributes & relationships have default values
    1021              : 
    1022         4946 :     if( __builtin_expect((check_re_read), 0) ) {
    1023            0 :       if(read_attributes) {
    1024            0 :         if(!read_attributes->empty()) {
    1025            0 :           for(auto i = read_attributes->begin(); i != read_attributes->end(); i = read_attributes->erase(i)) {
    1026            0 :             OksData d = (*i)->p_init_data;
    1027            0 :             try {
    1028            0 :               OksData *d2(GetAttributeValue((*i)->get_name()));
    1029            0 :               if(*d2 != d) {
    1030            0 :                 *d2 = d;
    1031              :                 was_updated = true;
    1032              :               }
    1033              :             }
    1034            0 :             catch(oks::exception& ex) {
    1035            0 :               throw oks::ObjectInitError(this, std::string("cannot reset default value of attribute \"") + (*i)->get_name() + '\"', ex);
    1036            0 :             }
    1037            0 :           }
    1038              :         }
    1039              : 
    1040            0 :         delete read_attributes;
    1041              :       }
    1042              : 
    1043            0 :       if(read_relationships) {
    1044            0 :         if(!read_relationships->empty()) {
    1045            0 :           for(auto i = read_relationships->begin(); i != read_relationships->end(); i = read_relationships->erase(i)) {
    1046            0 :             OksData d; d.ReadFrom(*i);
    1047            0 :             try {
    1048            0 :               OksData *d2(GetRelationshipValue((*i)->get_name()));
    1049            0 :               if(d2 && *d2 != d) {
    1050            0 :                 *d2 = d;
    1051              :                 was_updated = true;
    1052              :               }
    1053              :             }
    1054            0 :             catch(oks::exception& ex) {
    1055            0 :               throw oks::ObjectInitError(this, std::string("cannot reset value of relationship \"") + (*i)->get_name() + '\"', ex);
    1056            0 :             }
    1057            0 :           }
    1058              :         }
    1059              : 
    1060            0 :         delete read_relationships;
    1061              :       }
    1062              :     }
    1063              :   }
    1064              :   else {
    1065            0 :     size_t count(0);
    1066              : 
    1067            0 :     if(c == 0) {
    1068            0 :       throw std::runtime_error(
    1069              :         "  *****     Can't read objects of undefined class     *****\n"
    1070              :         "  *****    from file saved in non-extended format.    *****\n"
    1071              :         "  ***** Fix the problem first. Processing terminated. *****\n"
    1072            0 :       );
    1073              :     }
    1074              : 
    1075            0 :     if(c->p_all_attributes) {
    1076            0 :       for(const auto& i : *c->p_all_attributes) {
    1077            0 :         if(check_re_read) {
    1078            0 :           OksData d;
    1079              : 
    1080            0 :           if(i->get_is_multi_values()) {
    1081            0 :             d.read(read_params, i, __get_num(read_params));
    1082              :           }
    1083              :           else {
    1084            0 :             d.read(read_params, i);
    1085              :           }
    1086              : 
    1087            0 :           if(d != data[count]) {
    1088            0 :             data[count] = d;
    1089              :             was_updated = true;
    1090              :           }
    1091            0 :         }
    1092              :         else {
    1093            0 :           if(i->get_is_multi_values()) {
    1094            0 :             data[count].read(read_params, i, __get_num(read_params));
    1095              :           }
    1096              :           else {
    1097            0 :             data[count].read(read_params, i);
    1098              :           }
    1099              :         }
    1100              : 
    1101            0 :         count++;
    1102              :       }
    1103              :     }
    1104              : 
    1105            0 :     if(c->p_all_relationships) {
    1106            0 :       for(const auto& i : *c->p_all_relationships) {
    1107            0 :         if(check_re_read) {
    1108            0 :           OksData d;
    1109              : 
    1110            0 :           if(i->get_high_cardinality_constraint() == OksRelationship::Many) {
    1111            0 :             d.read(read_params, i, __get_num(read_params));
    1112              :           }
    1113              :           else {
    1114            0 :             d.read(read_params, i);
    1115              :           }
    1116              : 
    1117            0 :           if(d != data[count]) {
    1118            0 :             data[count] = d;
    1119              :             was_updated = true;
    1120              :           }
    1121            0 :         }
    1122              :         else {
    1123            0 :           if(i->get_high_cardinality_constraint() == OksRelationship::Many) {
    1124            0 :             data[count].read(read_params, i, __get_num(read_params));
    1125              :           }
    1126              :           else {
    1127            0 :             data[count].read(read_params, i);
    1128              :           }
    1129              :         }
    1130              : 
    1131            0 :         count++;
    1132              :       }
    1133              :     }
    1134              : 
    1135              :       // read tag closing object
    1136              : 
    1137            0 :     try {
    1138            0 :       const char * tag_start = read_params.s.get_tag_start();
    1139              :     
    1140            0 :       if(*tag_start != '/' || memcmp(tag_start + 1, read_params.object_tag, read_params.object_tag_len)) {
    1141            0 :         std::ostringstream s;
    1142            0 :         s << "Failed to read obj close tag (\'/" << read_params.object_tag << "\' is expected)";
    1143            0 :         throw std::runtime_error( s.str().c_str() );
    1144            0 :       }
    1145              :     }
    1146            0 :     catch (oks::exception & e) {
    1147            0 :       throw oks::FailedRead("object close tag", e);
    1148            0 :     }
    1149            0 :     catch (std::exception & e) {
    1150            0 :       throw oks::FailedRead("object close tag", e.what());
    1151            0 :     }
    1152              :   }
    1153              : 
    1154         4946 :   if(was_updated) change_notify();
    1155         4946 : }
    1156              : 
    1157         4948 : OksObject::OksObject(const oks::ReadFileParams& read_params, OksClass * c, const std::string& id) : data(nullptr), p_duplicated_object_id_idx(-1)
    1158              : {
    1159         4948 :   if(c) {
    1160         4948 :     OSK_PROFILING(OksProfiler::ObjectStreamConstructor, c->p_kernel)
    1161         4948 :   }
    1162              : 
    1163         4948 :   uid.object_id = id;
    1164         4948 :   uid.class_id = c;
    1165              : 
    1166         4948 :   std::shared_lock lock(read_params.oks_kernel->p_schema_mutex);  // protect schema and all objects from changes
    1167              : 
    1168         4947 :   if(c) {
    1169         4947 :     init1(read_params.f);  // can throw exception; to be caught by caller (i.e. OksKernel)
    1170              :   }
    1171              : 
    1172         4947 :   read_body(read_params, false);
    1173              : 
    1174         4946 :   if(c) {
    1175         4946 :     init3(c);
    1176              :   }
    1177         4948 : }
    1178              : 
    1179              : 
    1180           27 : OksObject::OksObject(const OksClass* c, const char *object_id, bool skip_init) : data(nullptr), p_duplicated_object_id_idx(-1)
    1181              : {
    1182           27 :   OSK_PROFILING(OksProfiler::ObjectNormalConstructor, c->p_kernel)
    1183              : 
    1184           27 :   if(!c->p_kernel->get_active_data()) {
    1185            0 :     Oks::error_msg("OksObject::OksObject(const OksClass*, const char *)") << "  Can not create new object since there is no active data file\n";
    1186            0 :     return;
    1187              :   }
    1188              : 
    1189           27 :   uid.class_id = c;
    1190           27 :   uid.object_id = (object_id ? object_id : "");
    1191              : 
    1192           27 :   try {
    1193           27 :     init1();
    1194              :   }
    1195            0 :   catch(oks::exception& ex) {
    1196            0 :     uid.class_id = nullptr;
    1197            0 :     throw;
    1198            0 :   }
    1199              : 
    1200           27 :   init2(skip_init);
    1201           27 :   init3();
    1202              : 
    1203           27 :   file->set_updated();
    1204           27 : }
    1205              : 
    1206            0 : OksObject::OksObject(const OksObject& parentObj, const char *object_id) : data(nullptr), p_duplicated_object_id_idx(-1)
    1207              : {
    1208            0 :   OSK_PROFILING(OksProfiler::ObjectCopyConstructor, parentObj.uid.class_id->p_kernel)
    1209              : 
    1210            0 :   if(!parentObj.uid.class_id->p_kernel->get_active_data()) {
    1211            0 :     Oks::error_msg("OksObject::OksObject(const OksObject&, const char *)") << "  Can not create new object since there is no active data file\n";
    1212            0 :     return;
    1213              :   }
    1214              : 
    1215            0 :   const OksClass *c = uid.class_id = parentObj.uid.class_id;
    1216              : 
    1217            0 :   uid.object_id = (object_id ? object_id : "");
    1218              : 
    1219            0 :   try {
    1220            0 :     init1();
    1221              :   }
    1222            0 :   catch(oks::exception& ex) {
    1223            0 :     uid.class_id = nullptr;
    1224            0 :     throw;
    1225            0 :   }
    1226              : 
    1227              :   
    1228            0 :   int count        = c->p_instance_size;
    1229            0 :   int num_of_attr = c->p_all_attributes->size();
    1230            0 :   int j    = 0;
    1231              : 
    1232            0 :   while(j < num_of_attr) {
    1233            0 :     data[j] = parentObj.data[j];
    1234            0 :     j++;
    1235              :   }
    1236              : 
    1237            0 :   try {
    1238            0 :     std::list<OksRelationship *>::iterator i = c->p_all_relationships->begin();
    1239              : 
    1240            0 :     while(j < count) {
    1241            0 :       const OksRelationship * r = *i;
    1242            0 :       const char * relationshipName = r->get_name().c_str();
    1243              : 
    1244            0 :       if(parentObj.data[j].type == OksData::list_type) {
    1245            0 :         data[j].Set(new OksData::List());
    1246              :                   
    1247            0 :         if(parentObj.data[j].data.LIST) {
    1248            0 :           for(const auto& i2 : *parentObj.data[j].data.LIST) {
    1249            0 :             if(i2->type == OksData::object_type)
    1250            0 :               AddRelationshipValue(relationshipName, i2->data.OBJECT);
    1251              :             else {
    1252            0 :               OksData * uid_d = new OksData();
    1253            0 :               uid_d = i2;
    1254            0 :               data[j].data.LIST->push_back(uid_d);
    1255              :             }
    1256              :           }
    1257              :         }
    1258              :       }
    1259              :       else {
    1260            0 :         if(parentObj.data[j].type == OksData::object_type) {
    1261            0 :           data[j].Set((OksObject *)0);
    1262            0 :           SetRelationshipValue(relationshipName, parentObj.data[j].data.OBJECT);
    1263              :         }
    1264              :         else
    1265            0 :           data[j] = parentObj.data[j];
    1266              :       }
    1267            0 :       ++j;
    1268            0 :       ++i;
    1269              :     }
    1270              :   }
    1271            0 :   catch(oks::exception& ex) {
    1272            0 :     uid.class_id = nullptr;
    1273            0 :     throw;
    1274            0 :   }
    1275              :   
    1276            0 :   init3();
    1277              : 
    1278            0 :   file->set_updated();
    1279            0 : }
    1280              : 
    1281            0 : OksObject::OksObject(size_t offset, const OksData *d) : data(nullptr), p_duplicated_object_id_idx(-1)
    1282              : {
    1283            0 :   uid.class_id = nullptr;
    1284            0 :   data = new OksData[offset+1];
    1285            0 :   memcpy(static_cast<void *>(&data[offset]), static_cast<const void *>(d), sizeof(OksData));
    1286            0 : }
    1287              : 
    1288            0 : OksObject::OksObject(OksClass * c, const std::string& id, void * user_data, int32_t int32_id, int32_t duplicated_object_id_idx, OksFile * f) :
    1289            0 :   data            (new OksData[c->p_instance_size]),
    1290            0 :   p_user_data     (user_data),
    1291            0 :   p_int32_id      (int32_id),
    1292            0 :   p_duplicated_object_id_idx (duplicated_object_id_idx),
    1293            0 :   file            (f)
    1294              :   
    1295              : {
    1296            0 :   uid.class_id = c;
    1297            0 :   uid.object_id = id;
    1298              : 
    1299              :     // set OksData type to unknown
    1300            0 :   if(size_t data_size = c->p_instance_size * sizeof(OksData*)) {
    1301            0 :     bzero(data, data_size);
    1302              :   }
    1303            0 : }
    1304              : 
    1305              : void
    1306            0 : OksObject::destroy(OksObject *o, bool fast)
    1307              : {
    1308            0 :   if(!o->uid.class_id || fast) {
    1309            0 :     delete o;
    1310            0 :     return;
    1311              :   }
    1312              : 
    1313              :     // check lack of references on object
    1314              : 
    1315            0 :   std::unique_ptr<std::ostringstream> error_text;
    1316              : 
    1317            0 :   const OksClass::Map & classes = o->uid.class_id->p_kernel->classes();
    1318              : 
    1319            0 :   for(OksClass::Map::const_iterator i = classes.begin(); i != classes.end(); ++i) {
    1320            0 :     OksClass *c(i->second);
    1321            0 :     if(c->p_all_relationships == nullptr || c->p_all_relationships->empty()) continue;
    1322              : 
    1323            0 :     if(const OksObject::Map * objects = c->objects()) {
    1324            0 :       for(OksObject::Map::const_iterator j = objects->begin(); j != objects->end(); ++j) {
    1325            0 :         size_t offset = c->number_of_all_attributes();
    1326            0 :         for(std::list<OksRelationship *>::iterator k = c->p_all_relationships->begin(); k != c->p_all_relationships->end(); ++k, ++offset) {
    1327            0 :           OksData * d = j->second->data + offset;
    1328            0 :           OksObject * ref = nullptr;
    1329              : 
    1330            0 :           if(d->type == OksData::object_type && d->data.OBJECT == o) {
    1331              :             ref = j->second;
    1332              :           }
    1333            0 :           else if(d->type == OksData::list_type && d->data.LIST != nullptr) {
    1334            0 :             for(const auto& l : *d->data.LIST) {
    1335            0 :               if(l->type == OksData::object_type && l->data.OBJECT == o) {
    1336            0 :                 ref = j->second;
    1337            0 :                 break;
    1338              :               }
    1339              :             }
    1340              :           }
    1341              : 
    1342            0 :           if(ref) {
    1343            0 :             if(!error_text.get()) {
    1344            0 :               error_text.reset(new std::ostringstream());
    1345            0 :               *error_text << "since it is referenced by:";
    1346              :             }
    1347            0 :             *error_text << "\n   * object " << ref << " via relationship \"" << (*k)->get_name() << '\"';
    1348              :           }
    1349              :         }
    1350              :       }
    1351              :     }
    1352              :   }
    1353              : 
    1354            0 :   if(error_text.get()) {
    1355            0 :     throw oks::FailedDestoyObject(o, error_text->str().c_str());
    1356              :   }
    1357              : 
    1358            0 :   try {
    1359            0 :     o->file->lock();
    1360            0 :     o->file->set_updated();
    1361              :   }
    1362            0 :   catch(oks::exception& ex) {
    1363            0 :     throw oks::FailedDestoyObject(o, ex);
    1364            0 :   }
    1365              : 
    1366            0 :   delete o;
    1367            0 : }
    1368              : 
    1369         4975 : OksObject::~OksObject()
    1370              : {
    1371         4975 :   OksClass * c = const_cast<OksClass *>(uid.class_id);
    1372              :   
    1373         4975 :   if(!c) {
    1374            0 :     if(data) {
    1375            0 :       delete [] data;
    1376              :     }
    1377              :   }
    1378              :   else {
    1379         4975 :     OksKernel * k = c->p_kernel;
    1380              : 
    1381         4975 :     OSK_PROFILING(OksProfiler::ObjectDestructor, k)
    1382              : 
    1383         4975 :       TLOG_DEBUG(4) << "destroy object " << this;
    1384              : 
    1385         4975 :     static std::mutex s_mutex;
    1386         4993 :     static std::set<OksObject *, std::less<OksObject *> > oset;
    1387              : 
    1388         4975 :     if(k->p_close_all == false) {
    1389            0 :       k->undefine(this);
    1390            0 :       std::lock_guard lock(s_mutex);
    1391            0 :       oset.insert(this);
    1392            0 :     }
    1393              : 
    1394         4975 :     delete_notify();
    1395              : 
    1396         4975 :     if(data) {
    1397         4975 :       if(c->p_all_relationships && !c->p_all_relationships->empty()) {
    1398         2367 :         OksData *di = data + c->number_of_all_attributes();
    1399              : 
    1400         9196 :         for(std::list<OksRelationship *>::iterator i = c->p_all_relationships->begin(); i != c->p_all_relationships->end(); ++i, ++di) {
    1401         6829 :           OksRelationship * r = *i;
    1402              : 
    1403         6829 :           if(r->get_is_composite() == false || k->p_close_all == true) continue;
    1404              : 
    1405            0 :           OksData * d = di;
    1406              : 
    1407            0 :           if(d->type == OksData::object_type && d->data.OBJECT) {
    1408            0 :             OksObject * o = d->data.OBJECT;
    1409              : 
    1410            0 :             if(!k->is_dangling(o)) {
    1411            0 :               o->remove_RCR(this, r);
    1412              : 
    1413            0 :               bool delete_obj(r->get_is_dependent() && !o->p_rcr);
    1414              : 
    1415            0 :               if(delete_obj) {
    1416            0 :                 std::lock_guard lock(s_mutex);
    1417            0 :                 delete_obj = (oset.find(o) == oset.end());
    1418            0 :               }
    1419              : 
    1420            0 :               if( delete_obj ) {
    1421            0 :                 delete o;
    1422            0 :                 d->data.OBJECT = nullptr;
    1423              :               }
    1424              :             }
    1425            0 :           }
    1426            0 :           else if(d->type == OksData::list_type && d->data.LIST) {
    1427            0 :             for(const auto& i2 : *d->data.LIST) {
    1428            0 :               if(i2->type == OksData::object_type && i2->data.OBJECT) {
    1429            0 :                 OksObject * o = i2->data.OBJECT;
    1430              : 
    1431            0 :                 if(!k->is_dangling(o)) {
    1432            0 :                   o->remove_RCR(this, r);
    1433              : 
    1434            0 :                   bool delete_obj(r->get_is_dependent() && !o->p_rcr);
    1435              : 
    1436            0 :                   if(delete_obj) {
    1437            0 :                     std::lock_guard lock(s_mutex);
    1438            0 :                     delete_obj = (oset.find(o) == oset.end());
    1439            0 :                   }
    1440              : 
    1441            0 :                   if( delete_obj ) {
    1442            0 :                     delete o;
    1443            0 :                     i2->data.OBJECT = nullptr;
    1444              :                   }
    1445              :                 }
    1446              :               }
    1447              :             }
    1448              :           }
    1449              :         }
    1450              :       }
    1451              : 
    1452         4975 :       if(c->p_indices) {
    1453            0 :         for(const auto& i : *c->p_indices)
    1454            0 :           i.second->remove_obj(this);
    1455              :       }
    1456              : 
    1457         4975 :       int count = c->p_instance_size;
    1458        24573 :       while(count--) data[count].Clear();
    1459              : 
    1460        24573 :       delete[] data;
    1461              : 
    1462         4975 :       if(k->p_close_all == false) {
    1463            0 :         try {
    1464            0 :           c->remove(this);
    1465              :         }
    1466            0 :         catch(oks::exception& ex) {
    1467            0 :           Oks::error_msg("OksObject::~OksObject()") << ex.what() << std::endl;
    1468            0 :         }
    1469              :       }
    1470              :     }
    1471              : 
    1472         4975 :     if(p_rcr) {
    1473         4717 :       for(std::list<OksRCR *>::const_iterator i = p_rcr->begin(); i != p_rcr->end(); ++i) {
    1474         2826 :         delete *i;
    1475              :       }
    1476              : 
    1477         1891 :       delete p_rcr;
    1478              :     }
    1479              : 
    1480         4975 :     if(k->p_close_all == false) {
    1481            0 :       std::lock_guard lock(s_mutex);
    1482            0 :       oset.erase(this);
    1483            0 :     }
    1484         4975 :   }
    1485         4975 : }
    1486              : 
    1487              : 
    1488              : void
    1489            0 : OksObject::set_file(OksFile * f, bool update_owner)
    1490              : {
    1491            0 :   if(file != f) {
    1492            0 :     try {
    1493            0 :       if(update_owner) {
    1494            0 :         file->lock();
    1495            0 :         file->set_updated();
    1496              :       }
    1497              : 
    1498            0 :       f->lock();
    1499            0 :       f->set_updated();
    1500              : 
    1501            0 :       file = f;
    1502              :     }
    1503            0 :     catch(oks::exception& ex) {
    1504            0 :       throw oks::CanNotSetFile(0, this, *f, ex);
    1505            0 :     }
    1506              :   }
    1507            0 : }
    1508              : 
    1509              : 
    1510              : void
    1511            0 : OksObject::set_id(const std::string &new_id)
    1512              : {
    1513            0 :   if(new_id == uid.object_id) return;
    1514              : 
    1515            0 :   try {
    1516            0 :     oks::validate_not_empty(new_id, "object id");
    1517              :   }
    1518            0 :   catch(std::exception& ex) {
    1519            0 :     throw oks::FailedRenameObject(this, ex.what());
    1520            0 :   }
    1521              : 
    1522            0 :   OksClass *c = const_cast<OksClass *>(uid.class_id);
    1523              : 
    1524            0 :   if(c->get_object(new_id)) {
    1525            0 :     throw oks::FailedRenameObject(this, "the object with such identity already exists");
    1526              :   }
    1527              : 
    1528              : 
    1529              :     // build set of updated files and list of updated objects
    1530              : 
    1531            0 :   std::set<OksFile *> updated_files;
    1532            0 :   std::list<OksObject *> updated_objects;
    1533              : 
    1534            0 :   updated_files.insert(file);
    1535            0 :   updated_objects.push_back(this);
    1536              : 
    1537            0 :   const OksClass::Map & classes = GetClass()->get_kernel()->classes();
    1538              : 
    1539            0 :   for(OksClass::Map::const_iterator i = classes.begin(); i != classes.end(); ++i) {
    1540            0 :     const OksClass * c(i->second);
    1541            0 :     const OksObject::Map * objs = c->objects();
    1542            0 :     size_t d_end = c->p_instance_size;
    1543            0 :     size_t d_begin = c->p_all_attributes->size();
    1544            0 :     if(objs && (d_end != d_begin)) {
    1545            0 :       for(OksObject::Map::const_iterator j = objs->begin(); j != objs->end(); ++j) {
    1546            0 :         OksObject * o(j->second);
    1547              : 
    1548              :           // avoid multiple notification, when renamed object contains references to self
    1549            0 :         if(o != this) {
    1550            0 :           for(size_t k = d_begin; k != d_end && o; ++k) {
    1551            0 :             const OksData & d = o->data[k];
    1552            0 :             if(d.type == OksData::object_type && d.data.OBJECT == this) {
    1553            0 :               updated_objects.push_back(o);
    1554            0 :               updated_files.insert(o->file);
    1555            0 :               o = nullptr; // break loop
    1556              :             }
    1557            0 :             else if(d.type == OksData::list_type && d.data.LIST) {
    1558            0 :               const OksData::List & list = *d.data.LIST;
    1559            0 :               for(OksData::List::const_iterator l = list.begin(); l != list.end(); ++l) {
    1560            0 :                 OksData * d2 = *l;
    1561            0 :                 if(d2->type == OksData::object_type && d2->data.OBJECT == this) {
    1562            0 :                   updated_objects.push_back(o);
    1563            0 :                   updated_files.insert(o->file);
    1564            0 :                   o = nullptr; // break loop
    1565            0 :                   break;
    1566              :                 }
    1567              :               }
    1568              :             }
    1569              :           }
    1570              :         }
    1571              :       }
    1572              :     }
    1573              :   }
    1574              : 
    1575            0 :   try {
    1576            0 :     for(std::set<OksFile *>::const_iterator i = updated_files.begin(); i != updated_files.end(); ++i) {
    1577            0 :       (*i)->lock();
    1578              :     }
    1579              :   }
    1580            0 :   catch(oks::exception& ex) {
    1581            0 :     throw oks::FailedRenameObject(this, ex);
    1582            0 :   }
    1583              : 
    1584            0 :   try {
    1585            0 :     c->remove(this);          // remove object from hash table
    1586            0 :     uid.object_id = new_id;   // change id
    1587            0 :     c->add(this);             // add object to hash table
    1588              :   }
    1589            0 :   catch(oks::exception& ex) {
    1590            0 :     throw oks::FailedRenameObject(this, ex);
    1591            0 :   }
    1592              : 
    1593            0 :   for(std::set<OksFile *>::const_iterator i = updated_files.begin(); i != updated_files.end(); ++i) {
    1594            0 :     (*i)->set_updated();
    1595              :   }
    1596              : 
    1597            0 :   if(get_change_notify()) {
    1598            0 :     for(std::list<OksObject *>::const_iterator i = updated_objects.begin(); i != updated_objects.end(); ++i) {
    1599            0 :       (*i)->change_notify();
    1600              :     }
    1601              :   }
    1602            0 : }
    1603              : 
    1604              : bool
    1605            0 : OksObject::are_equal_fast(const OksObject * o1, const OksObject * o2)
    1606              : {
    1607            0 :   if (o1 == o2)
    1608              :     return true;
    1609              : 
    1610            0 :   if (o1 == nullptr || o2 == nullptr)
    1611              :     return false;
    1612              : 
    1613            0 :   return (o1->uid.class_id->get_name() == o2->uid.class_id->get_name() && o1->uid.object_id == o2->uid.object_id);
    1614              : }
    1615              : 
    1616              : bool
    1617            0 : OksObject::operator==(const OksObject &o) const
    1618              : {
    1619            0 :   if (are_equal_fast(this, &o) == false)
    1620              :     return false;
    1621              : 
    1622            0 :   OSK_PROFILING(OksProfiler::ObjectOperatorEqual, uid.class_id->p_kernel)
    1623              : 
    1624              :   size_t count(0);
    1625              : 
    1626            0 :   while (count < uid.class_id->p_instance_size)
    1627              :     {
    1628            0 :       if (data[count] == o.data[count])
    1629            0 :         count++;
    1630              :       else
    1631              :         return false;
    1632              :     }
    1633              : 
    1634              :   return true;
    1635            0 : }
    1636              : 
    1637              : 
    1638              : std::ostream&
    1639           36 : operator<<(std::ostream& s, const OksObject * o)
    1640              : {
    1641           36 :   s << '\"';
    1642              :   
    1643           36 :   if(o)
    1644           36 :     s << o->GetId() << '@' << o->GetClass()->get_name();
    1645              :   else
    1646            0 :     s << "(null-obj)";
    1647              : 
    1648           36 :   s << '\"';
    1649              :   
    1650           36 :   return s;
    1651              : }
    1652              : 
    1653              : 
    1654              : std::ostream&
    1655            0 : operator<<(std::ostream& s, const OksObject& o)
    1656              : {
    1657            0 :   OSK_PROFILING(OksProfiler::ObjectOperatorOut, o.uid.class_id->p_kernel)
    1658              :   
    1659            0 :   const OksClass * c = o.uid.class_id;
    1660              : 
    1661            0 :   s << "Object " << &o << std::endl;
    1662              :   
    1663            0 :   OksData * di = o.data;
    1664              : 
    1665              : 
    1666              :     // print attributes
    1667              : 
    1668            0 :   {
    1669            0 :     const std::list<OksAttribute *> * alist = c->all_attributes();
    1670              : 
    1671            0 :     if(alist && !alist->empty()) {
    1672            0 :       s << " Attributes are:\n";
    1673              : 
    1674            0 :       for(std::list<OksAttribute *>::const_iterator i = alist->begin(); i != alist->end(); ++i) {
    1675            0 :         if((*i)->is_integer() && (*i)->get_format() != OksAttribute::Dec) {
    1676            0 :           s.setf(((*i)->get_format() == OksAttribute::Hex ? std::ios::hex : std::ios::oct), std::ios::basefield);
    1677            0 :           s.setf(std::ios::showbase);
    1678              :         }
    1679              : 
    1680            0 :         s << "  " << (*i)->get_name() << ": " << *di++ << std::endl;
    1681              : 
    1682            0 :         if((*i)->is_integer() && (*i)->get_format() != OksAttribute::Dec) {
    1683            0 :           s.setf(std::ios::dec, std::ios::basefield);
    1684            0 :           s.unsetf(std::ios::showbase);
    1685              :         }
    1686              :       }
    1687              :     }
    1688              :     else
    1689            0 :       s << " There are no attributes\n";
    1690              :   }
    1691              : 
    1692              : 
    1693              :     // print relationships
    1694              : 
    1695            0 :   {
    1696            0 :     const std::list<OksRelationship *> * rlist = c->all_relationships();
    1697              : 
    1698            0 :     if(rlist && !rlist->empty()) {
    1699            0 :       s << " Relationships are:\n";
    1700              :         
    1701            0 :       for(std::list<OksRelationship *>::const_iterator i = rlist->begin(); i != rlist->end(); ++i)
    1702            0 :         s << "  " << (*i)->get_name() << ": " << *di++ << std::endl;
    1703              :     }
    1704              :     else
    1705            0 :       s << " There are no relationships\n";
    1706              :   }
    1707              : 
    1708            0 :   return s;
    1709            0 : }       
    1710              : 
    1711              : 
    1712              :   //
    1713              :   // Remove dangling objects before writing to file
    1714              :   // Avoid unnecessary additional checks
    1715              :   //
    1716              : 
    1717              : static bool
    1718            0 : trim_dangling(OksData & d, const OksKernel & kernel)
    1719              : {
    1720            0 :   if (d.type == OksData::object_type)
    1721              :     {
    1722            0 :       if (d.data.OBJECT && kernel.is_dangling(d.data.OBJECT))
    1723              :         {
    1724            0 :           d.data.OBJECT = nullptr;
    1725              :         }
    1726              : 
    1727            0 :       return (d.data.OBJECT != nullptr);
    1728              :     }
    1729            0 :   else if(d.type == OksData::list_type)
    1730              :     {
    1731            0 :       if (d.data.LIST)
    1732              :         {
    1733            0 :           for (OksData::List::iterator i = d.data.LIST->begin(); i != d.data.LIST->end();)
    1734              :             {
    1735            0 :               OksData * d2 = *i;
    1736            0 :               if (d2->type == OksData::object_type)
    1737              :                 {
    1738            0 :                   if (d2->data.OBJECT == nullptr || kernel.is_dangling(d2->data.OBJECT))
    1739              :                     {
    1740            0 :                       i = d.data.LIST->erase(i);
    1741            0 :                       delete d2;
    1742            0 :                       continue;
    1743              :                     }
    1744              :                 }
    1745            0 :               ++i;
    1746              :             }
    1747              : 
    1748            0 :           return (!d.data.LIST->empty());
    1749              :         }
    1750              :       else
    1751              :         return false;
    1752              :     }
    1753              : 
    1754              :   return true;
    1755              : }
    1756              : 
    1757              : 
    1758              : void
    1759            0 : OksObject::put_object_attributes(OksXmlOutputStream& xmls, const OksData& d)
    1760              : {
    1761            0 :   const std::string * class_name = nullptr;
    1762            0 :   const std::string * object_id = nullptr;
    1763              : 
    1764            0 :   if(d.type == OksData::object_type)
    1765              :     {
    1766            0 :       class_name = &d.data.OBJECT->GetClass()->get_name();
    1767            0 :       object_id = &d.data.OBJECT->GetId();
    1768              :     }
    1769            0 :   else if(d.type == OksData::uid_type)
    1770              :     {
    1771            0 :       class_name = &d.data.UID.class_id->get_name();
    1772            0 :       object_id = d.data.UID.object_id;
    1773              :     }
    1774              :   else
    1775              :     {
    1776            0 :       class_name = d.data.UID2.class_id;
    1777            0 :       object_id = d.data.UID2.object_id;
    1778              :     }
    1779              : 
    1780            0 :   xmls.put_attribute(class_xml_attribute, sizeof(class_xml_attribute)-1, class_name->c_str());
    1781            0 :   xmls.put_attribute(id_xml_attribute, sizeof(id_xml_attribute)-1, object_id->c_str());
    1782            0 : }
    1783              : 
    1784              : void
    1785            0 : OksObject::put(OksXmlOutputStream& xmls, bool force_defaults) const
    1786              : {
    1787            0 :   OSK_PROFILING(OksProfiler::ObjectPutObject, uid.class_id->p_kernel)
    1788              : 
    1789            0 :   try
    1790              :     {
    1791            0 :       const OksClass * class_id = uid.class_id;
    1792              : 
    1793            0 :       xmls.put_start_tag(obj_xml_tag, sizeof(obj_xml_tag)-1);
    1794              : 
    1795            0 :       xmls.put_attribute(class_xml_attribute, sizeof(class_xml_attribute)-1, class_id->get_name().c_str());
    1796            0 :       xmls.put_attribute(id_xml_attribute, sizeof(id_xml_attribute)-1, uid.object_id.c_str());
    1797              : 
    1798            0 :       xmls.put_eol();
    1799              : 
    1800            0 :       int count = 0;
    1801              : 
    1802            0 :       if (auto alist = class_id->all_attributes())
    1803              :         {
    1804            0 :           for (const auto& a : *alist)
    1805              :             {
    1806            0 :               OksData& d = data[count++];
    1807              : 
    1808              :               // store all attributes with value != initial, or date/time types which default values = now(), or non-numbers or numbers with non-zero values
    1809            0 :               if (force_defaults || d != a->p_init_data || a->p_data_type == OksData::date_type || a->p_data_type == OksData::time_type || (a->get_init_value().empty() == false && (a->get_is_multi_values() == true || a->is_number() == false || a->get_init_value().size() > 1 || a->get_init_value()[0] != '0')))
    1810              :                 {
    1811            0 :                   xmls.put_raw(' ');
    1812            0 :                   xmls.put_start_tag(attribute_xml_tag, sizeof(attribute_xml_tag) - 1);
    1813            0 :                   xmls.put_attribute(name_xml_attribute, sizeof(name_xml_attribute) - 1, a->get_name().c_str());
    1814            0 :                   xmls.put_attribute(type_xml_attribute, sizeof(type_xml_attribute) - 1, a->get_type().c_str());
    1815              : 
    1816            0 :                   if (a->is_integer() && a->get_format() != OksAttribute::Dec)
    1817              :                     {
    1818            0 :                       xmls.get_stream().setf((a->get_format() == OksAttribute::Hex ? std::ios::hex : std::ios::oct), std::ios::basefield);
    1819            0 :                       xmls.get_stream().setf(std::ios::showbase);
    1820              :                     }
    1821              : 
    1822            0 :                   if (a->get_is_multi_values() == false)
    1823              :                     {
    1824            0 :                       xmls.put_attribute(value_xml_attribute, sizeof(value_xml_attribute) - 1, d);
    1825            0 :                       xmls.put_end_tag();
    1826              :                     }
    1827              :                   else
    1828              :                     {
    1829            0 :                       if (d.data.LIST->empty())
    1830              :                         {
    1831            0 :                           xmls.put_end_tag();
    1832              :                         }
    1833              :                       else
    1834              :                         {
    1835            0 :                           if(a->p_ordered)
    1836            0 :                             d.sort();
    1837              : 
    1838            0 :                           xmls.put_eol();
    1839              : 
    1840            0 :                           for (const auto& x : *d.data.LIST)
    1841              :                             {
    1842            0 :                               xmls.put_raw(' ');
    1843            0 :                               xmls.put_raw(' ');
    1844            0 :                               xmls.put_start_tag(data_xml_tag, sizeof(data_xml_tag) - 1);
    1845            0 :                               xmls.put_attribute(value_xml_attribute, sizeof(value_xml_attribute) - 1, *x);
    1846            0 :                               xmls.put_end_tag();
    1847              :                             }
    1848              : 
    1849            0 :                           xmls.put_raw(' ');
    1850            0 :                           xmls.put_last_tag(attribute_xml_tag, sizeof(attribute_xml_tag) - 1);
    1851              :                         }
    1852              :                     }
    1853              : 
    1854            0 :                   if (a->is_integer() && a->get_format() != OksAttribute::Dec)
    1855              :                     {
    1856            0 :                       xmls.get_stream().setf(std::ios::dec, std::ios::basefield);
    1857            0 :                       xmls.get_stream().unsetf(std::ios::showbase);
    1858              :                     }
    1859              :                 }
    1860              :             }
    1861              :         }
    1862              : 
    1863            0 :       if (const std::list<OksRelationship *> * rlist = class_id->all_relationships())
    1864              :         {
    1865            0 :           for (const auto& r : *rlist)
    1866              :             {
    1867            0 :               OksData& rel_data = data[count++];
    1868              : 
    1869            0 :               if (trim_dangling(rel_data, *class_id->p_kernel))
    1870              :                 {
    1871            0 :                   xmls.put_raw(' ');
    1872            0 :                   xmls.put_start_tag(relationship_xml_tag, sizeof(relationship_xml_tag) - 1);
    1873            0 :                   xmls.put_attribute(name_xml_attribute, sizeof(name_xml_attribute) - 1, r->get_name().c_str());
    1874              : 
    1875            0 :                   if (r->get_high_cardinality_constraint() != OksRelationship::Many)
    1876              :                     {
    1877            0 :                       put_object_attributes(xmls, rel_data);
    1878            0 :                       xmls.put_end_tag();
    1879              :                     }
    1880              :                   else
    1881              :                     {
    1882            0 :                       if(r->p_ordered)
    1883            0 :                         rel_data.sort();
    1884              : 
    1885            0 :                       xmls.put_eol();
    1886              : 
    1887            0 :                       for (const auto& x : *rel_data.data.LIST)
    1888              :                         {
    1889            0 :                           xmls.put_raw(' ');
    1890            0 :                           xmls.put_raw(' ');
    1891            0 :                           xmls.put_start_tag(ref_xml_tag, sizeof(ref_xml_tag) - 1);
    1892            0 :                           put_object_attributes(xmls, *x);
    1893            0 :                           xmls.put_end_tag();
    1894              :                         }
    1895              : 
    1896            0 :                       xmls.put_raw(' ');
    1897            0 :                       xmls.put_last_tag(relationship_xml_tag, sizeof(relationship_xml_tag) - 1);
    1898              :                     }
    1899              :                 }
    1900              :             }
    1901              :         }
    1902              : 
    1903            0 :       xmls.put_last_tag(obj_xml_tag, sizeof(obj_xml_tag) - 1);
    1904              :     }
    1905            0 :   catch (oks::exception & ex)
    1906              :     {
    1907            0 :       throw(oks::FailedSaveObject(this, ex));
    1908            0 :     }
    1909            0 :   catch (std::ios_base::failure & ex)
    1910              :     {
    1911            0 :       throw(oks::FailedSaveObject(this, std::string("caught std::ios_base::failure exception \"") + ex.what() + '\"'));
    1912            0 :     }
    1913            0 :   catch (std::exception & ex)
    1914              :     {
    1915            0 :       throw(oks::FailedSaveObject(this, std::string("caught std::exception \"") + ex.what() + '\"'));
    1916            0 :     }
    1917            0 :   catch (...)
    1918              :     {
    1919            0 :       throw(oks::FailedSaveObject(this, "unknown reason"));
    1920            0 :     }
    1921            0 : }
    1922              : 
    1923              : OksData *
    1924         2599 : OksObject::GetAttributeValue(const std::string& name) const
    1925              : {
    1926         2599 :   OksDataInfo::Map::const_iterator i = uid.class_id->p_data_info->find(name);
    1927              : 
    1928         2599 :   if(i == uid.class_id->p_data_info->end()) {
    1929            0 :     std::ostringstream text;
    1930            0 :     text << "object " << this << " has no attribute \"" << name << '\"';
    1931            0 :     throw oks::ObjectGetError(this, false, name, text.str());
    1932            0 :   }
    1933              : 
    1934         2599 :   return GetAttributeValue(i->second);
    1935              : }
    1936              : 
    1937              : 
    1938              : void
    1939           11 : OksObject::SetAttributeValue(const OksDataInfo *odi, OksData *d)
    1940              : {
    1941           11 :   size_t offset = odi->offset;
    1942           11 :   const OksAttribute * a = odi->attribute;
    1943           11 :   OksData d2; // can be needed by type conversion
    1944              :   
    1945           11 :   if(data[offset].type != d->type) {
    1946            3 :     if(uid.class_id->get_kernel()->get_silence_mode() == false) {
    1947            0 :       Oks::warning_msg("OksObject::SetAttributeValue(const OksDataInfo *, OksData *)")
    1948            0 :         << "  * ODI attribute name: \'" << a->get_name() << "\'\n"
    1949            0 :            "  * ODI data offset: \'" << offset << "\'\n"
    1950            0 :            "  * OKS data (new): \'" << *d << "\' type: " << (int)d->type << "\n"
    1951            0 :            "  * OKS data (old): \'" << data[offset] << "\' type: " << (int)data[offset].type << "\n"
    1952            0 :            "  Object " << this << ":\n"
    1953            0 :            "  Convert attribute \"" << a->get_name() << "\" value since the OksData::type is invalid\n";
    1954              :     }
    1955              : 
    1956            3 :     d->cvt(&d2, a);
    1957              :     d=&d2;
    1958              :   }
    1959              : 
    1960           11 :   try {
    1961           11 :     d->check_range(a);
    1962              :   }
    1963            0 :   catch(oks::AttributeReadError & ex) {
    1964            0 :     throw oks::ObjectSetError(this, false, a->get_name(), ex);
    1965            0 :   }
    1966            0 :   catch(oks::AttributeRangeError & ex) {
    1967            0 :     throw oks::ObjectSetError(this, false, a->get_name(), ex);
    1968            0 :   }
    1969              : 
    1970           11 :   check_file_lock(a, 0);
    1971              : 
    1972              : 
    1973              :     // set value and update index if any
    1974              : 
    1975           11 :   {
    1976           11 :     OksIndex * i = nullptr;
    1977              : 
    1978           11 :     if(uid.class_id->p_indices) {
    1979            0 :       OksIndex::Map::iterator j = uid.class_id->p_indices->find(a);
    1980            0 :       if(j != uid.class_id->p_indices->end()) {
    1981            0 :         i = j->second;
    1982              :       }
    1983              :     }
    1984              : 
    1985            0 :     if(i) i->remove_obj(this);
    1986           11 :     data[offset] = *d;
    1987           11 :     if(i) i->insert(this);
    1988              :   }
    1989              : 
    1990           11 :   notify();
    1991           11 : }
    1992              : 
    1993              : 
    1994              : void
    1995           11 : OksObject::SetAttributeValue(const std::string& name, OksData *d)
    1996              : {
    1997           11 :   OksDataInfo::Map::iterator i = uid.class_id->p_data_info->find(name);
    1998              : 
    1999           11 :   if(i == uid.class_id->p_data_info->end()) {
    2000            0 :     std::ostringstream text;
    2001            0 :     text << "object " << this << " has no attribute \"" << name << '\"';
    2002            0 :     throw oks::ObjectSetError(this, false, name, text.str());
    2003            0 :   }
    2004              : 
    2005           11 :   SetAttributeValue(i->second, d);
    2006           11 : }
    2007              : 
    2008              : OksData *
    2009         1342 : OksObject::GetRelationshipValue(const std::string& name) const
    2010              : {
    2011         1342 :   OksDataInfo::Map::iterator i = uid.class_id->p_data_info->find(name);
    2012              :   
    2013         1342 :   if(i == uid.class_id->p_data_info->end()) {
    2014            0 :     std::ostringstream text;
    2015            0 :     text << "object " << this << " has no relationship \"" << name << '\"';
    2016            0 :     throw oks::ObjectGetError(this, false, name, text.str());
    2017            0 :   }
    2018              : 
    2019         1342 :   return GetRelationshipValue(i->second);
    2020              : }
    2021              : 
    2022              : 
    2023              : void
    2024            1 : OksObject::check_non_null(const OksRelationship *r, const OksObject *o)
    2025              : {
    2026            1 :   if(!o && r->get_low_cardinality_constraint() != OksRelationship::Zero) {
    2027            0 :     throw oks::ObjectSetError(
    2028              :       this, true, r->get_name(),
    2029              :       "set to (null) is not allowed since the relationship has non-zero low cardinality constraint"
    2030            0 :     );
    2031              :   }
    2032            1 : }
    2033              : 
    2034              : 
    2035              : void
    2036         2808 : OksObject::check_class_type(const OksRelationship *r, const OksClass *class_type)
    2037              : {
    2038         2808 :   if(!class_type) return;
    2039              : 
    2040         2808 :   const OksClass * rel_class_type = r->p_class_type;
    2041              : 
    2042         2808 :   if(class_type != rel_class_type) {
    2043          729 :     OksClass::FList * super_classes = class_type->p_all_super_classes;
    2044              : 
    2045          729 :     if(super_classes && !super_classes->empty()) {
    2046         1013 :       for(const auto& i : *super_classes) {
    2047         1013 :         if(rel_class_type == i) return;
    2048              :       }
    2049              :     }
    2050              : 
    2051            0 :     std::ostringstream text;
    2052            0 :     text << "set to an object of class \"" << class_type->get_name() << "\" is not allowed since the relationship's class type is \""
    2053            0 :          << r->get_type() << "\" and the class \"" << class_type->get_name() << "\" has no such superclass";
    2054            0 :     throw oks::ObjectSetError(this, true, r->get_name(), text.str());
    2055            0 :   }
    2056              : }
    2057              : 
    2058              : 
    2059              : void
    2060           10 : OksObject::SetRelationshipValue(const OksDataInfo *odi, OksData *d, bool skip_non_null_check)
    2061              : {
    2062           10 :   const OksRelationship * r = odi->relationship;
    2063           10 :   size_t offset = odi->offset;
    2064              : 
    2065           10 :   if(
    2066           10 :    (data[offset].type != d->type) &&
    2067            0 :    ( (d->type == OksData::list_type) || (data[offset].type == OksData::list_type) )
    2068              :   ) {
    2069            0 :     std::ostringstream text;
    2070            0 :     text << "OKS data type is invalid: must be " << (data[offset].type == OksData::object_type ? "an object)\n" : "a list); ")
    2071            0 :          << "offset: " << offset << "; new: \'" << *d << "\'; old:" << data[offset] << '\'';
    2072            0 :     throw oks::ObjectSetError(this, true, r->get_name(), text.str());
    2073            0 :   }
    2074              : 
    2075              :     // used to update RCRs
    2076              : 
    2077           10 :   OksObject::FSet added_objs;
    2078           10 :   OksObject::FSet removed_objs;
    2079              : 
    2080           10 :   if(d->type == OksData::object_type) {
    2081            1 :     OksObject * add_obj = d->data.OBJECT;
    2082            1 :     OksObject * rm_obj = (data[offset].type == OksData::object_type ? data[offset].data.OBJECT : 0);
    2083              : 
    2084            1 :     if(add_obj != rm_obj) {
    2085            1 :       if(add_obj) added_objs.insert(add_obj);
    2086            1 :       if(rm_obj) removed_objs.insert(rm_obj);
    2087              :     }
    2088              :     else {
    2089            0 :       return; // new and old values are equal, nothing to do
    2090              :     }
    2091              : 
    2092            1 :     if(skip_non_null_check == false) check_non_null(r, d->data.OBJECT);
    2093            1 :     check_class_type(r, d->data.OBJECT);
    2094              :   }
    2095            9 :   else if(d->type == OksData::list_type && d->data.LIST) {
    2096            9 :     if(d->data.LIST->empty()) {
    2097            1 :       if(r->get_low_cardinality_constraint() != OksRelationship::Zero && skip_non_null_check == false) {
    2098            0 :         throw oks::ObjectSetError(
    2099              :           this, true, r->get_name(),
    2100              :           "set to empty list is not allowed since the relationship has non-zero low cardinality constraint"
    2101            0 :         );
    2102              :       }
    2103              :     }
    2104              : 
    2105           30 :     for(const auto& i : *d->data.LIST) {
    2106           21 :       if(i->type != OksData::object_type && i->type != OksData::uid2_type && i->type != OksData::uid_type) {
    2107            0 :         throw oks::ObjectSetError(this, true, r->get_name(), "list contains non-objects");
    2108              :       }
    2109              : 
    2110           21 :       if(
    2111           21 :        (i->type == OksData::object_type && !i->data.OBJECT) ||
    2112           21 :        (i->type == OksData::uid2_type && !i->data.UID2.object_id) ||
    2113            0 :        (i->type == OksData::uid_type && !i->data.UID.object_id)
    2114              :       ) {
    2115            0 :         throw oks::ObjectSetError(this, true, r->get_name(), "list contains (null) object");
    2116              :       }
    2117              : 
    2118           21 :       if(i->type == OksData::object_type) {
    2119           21 :         if(OksObject * o2 = i->data.OBJECT) {
    2120           21 :           check_class_type(r, o2);
    2121           21 :           added_objs.insert(o2);
    2122              :         }
    2123              :       }
    2124              :     }
    2125            9 :   }
    2126              :   else {
    2127            0 :     throw oks::ObjectSetError(
    2128              :       this, true, r->get_name(),
    2129              :       "the OKS data type is invalid (must be a list of objects or an object)"
    2130            0 :     );
    2131              :   }
    2132              : 
    2133           10 :   check_file_lock(0, r);
    2134              : 
    2135              : 
    2136              :     // put old value to objects removed from relationship
    2137              : 
    2138           10 :   if(data[offset].type == OksData::list_type && data[offset].data.LIST) {
    2139           10 :       for(const auto& i : *data[offset].data.LIST) {
    2140            1 :         if(i->type == OksData::object_type && i->data.OBJECT) {
    2141            1 :           removed_objs.insert(i->data.OBJECT);
    2142              :         }
    2143              :       }
    2144              :   }
    2145              : 
    2146              : 
    2147              :     // remove RCRs
    2148              : 
    2149           11 :   for(const auto& i : removed_objs) {
    2150            1 :     if(added_objs.find(i) == added_objs.end()) {
    2151            1 :       TLOG_DEBUG(4) << "object " << i << " was removed from relationship";
    2152            1 :       i->remove_RCR(this, r);
    2153              :     }
    2154              :   }
    2155              : 
    2156              : 
    2157              :     // add RCRs
    2158              : 
    2159           32 :   for(OksObject::FSet::const_iterator i = added_objs.begin(); i != added_objs.end(); ++i) {
    2160           22 :     OksObject * add_obj = *i;
    2161           22 :     if(removed_objs.find(add_obj) == removed_objs.end()) {
    2162           22 :       TLOG_DEBUG(4) << "object " << add_obj << " was added to relationship";
    2163           22 :       try {
    2164           22 :         add_obj->add_RCR(this, r);
    2165              :       }
    2166            0 :       catch(oks::exception& ex) {
    2167              : 
    2168              :           // reset updated RCRs
    2169              : 
    2170            0 :         for(OksObject::FSet::const_iterator j = added_objs.begin(); j != i; ++j) {
    2171            0 :           if(removed_objs.find(*j) == removed_objs.end()) {
    2172            0 :             (*j)->remove_RCR(this, r);
    2173              :           }
    2174              :         }
    2175              : 
    2176            0 :         for(OksObject::FSet::const_iterator j = removed_objs.begin(); j != removed_objs.end(); ++j) {
    2177            0 :           if(added_objs.find(*j) == added_objs.end()) {
    2178            0 :             try { (*j)->add_RCR(this, r); } catch (oks::exception&) { ; }
    2179              :           }
    2180              :         }
    2181              : 
    2182            0 :         throw oks::ObjectSetError(this, true, r->get_name(), ex);
    2183            0 :       }
    2184              :     }
    2185              :   }
    2186              : 
    2187           10 :   data[offset] = *d;
    2188              : 
    2189           10 :   notify();
    2190           10 : }
    2191              : 
    2192              : 
    2193              : void
    2194           10 : OksObject::SetRelationshipValue(const std::string& name, OksData *d, bool skip_non_null_check)
    2195              : {
    2196           10 :   OksDataInfo::Map::iterator i = uid.class_id->p_data_info->find(name);
    2197              : 
    2198           10 :   if(i == uid.class_id->p_data_info->end()) {
    2199            0 :     std::ostringstream text;
    2200            0 :     text << "object " << this << " has no relationship \"" << name << '\"';
    2201            0 :     throw oks::ObjectSetError(this, true, name, text.str());
    2202            0 :   }
    2203              : 
    2204           10 :   SetRelationshipValue(i->second, d, skip_non_null_check);
    2205           10 : }
    2206              : 
    2207              :         
    2208              : void
    2209            0 : OksObject::SetRelationshipValue(const OksDataInfo *odi, OksObject *object)
    2210              : {
    2211            0 :   const OksRelationship *r = odi->relationship;
    2212            0 :   OksData& d(data[odi->offset]);
    2213              : 
    2214            0 :   if( r->get_high_cardinality_constraint() == OksRelationship::Many ) {
    2215            0 :     std::ostringstream text;
    2216            0 :     text << "cannot set value " << object << " since the relationship has \'many\' high cardinality constraint";
    2217            0 :     throw oks::ObjectSetError(this, true, r->get_name(), text.str());
    2218            0 :   }
    2219              : 
    2220            0 :   if(!object) {
    2221            0 :     check_non_null(r, object);
    2222              :   }
    2223              :   else {
    2224            0 :     check_class_type(r, object);
    2225              :   }
    2226              : 
    2227            0 :   check_file_lock(0, r);
    2228              : 
    2229            0 :   if((d.type == OksData::object_type) && d.data.OBJECT) {
    2230            0 :     d.data.OBJECT->remove_RCR(this, r);
    2231              :   }
    2232              : 
    2233            0 :   if(object) {
    2234            0 :     try {
    2235            0 :       object->add_RCR(this, r);
    2236              :     }
    2237            0 :     catch(oks::exception& ex) {
    2238              :       // restore RCR and throw exception
    2239            0 :       if((d.type == OksData::object_type) && d.data.OBJECT)
    2240            0 :         try { d.data.OBJECT->add_RCR(this, r); } catch(oks::exception& ex2) { ; }
    2241            0 :       throw oks::ObjectSetError(this, true, r->get_name(), ex);
    2242            0 :     }
    2243              :   }
    2244              : 
    2245            0 :   d.Set(object);
    2246              : 
    2247            0 :   notify();
    2248            0 : }
    2249              : 
    2250              : 
    2251              : void
    2252            0 : OksObject::SetRelationshipValue(const std::string& name, OksObject *o)
    2253              : {
    2254            0 :   OksDataInfo::Map::iterator i = uid.class_id->p_data_info->find(name);
    2255              : 
    2256            0 :   if(i == uid.class_id->p_data_info->end()) {
    2257            0 :     std::ostringstream text;
    2258            0 :     text << "object " << this << " has no relationship \"" << name << '\"';
    2259            0 :     throw oks::ObjectSetError(this, true, name, text.str());
    2260            0 :   }
    2261              : 
    2262            0 :   SetRelationshipValue(i->second, o);
    2263            0 : }
    2264              : 
    2265              :         
    2266              : 
    2267              : static bool
    2268            0 : cmp_data(OksData * d, OksData * d2)
    2269              : {
    2270            0 :   if(d->type == OksData::object_type && d2->type == OksData::object_type)
    2271            0 :     return ((d2->data.OBJECT == d->data.OBJECT) ? true : false);
    2272              : 
    2273            0 :   const std::string& object_id =
    2274            0 :     d->type == OksData::object_type  ? d->data.OBJECT->GetId() :
    2275              :     d->type == OksData::uid2_type    ? *(static_cast<std::string*>(d->data.UID2.object_id)) :
    2276            0 :     *(static_cast<std::string*>(d->data.UID.object_id));
    2277              : 
    2278            0 :   const std::string& object_id2 =
    2279            0 :     d2->type == OksData::object_type ? d2->data.OBJECT->GetId() :
    2280              :     d2->type == OksData::uid2_type   ? *(static_cast<std::string*>(d2->data.UID2.object_id)) :
    2281            0 :     *(static_cast<std::string*>(d2->data.UID.object_id));
    2282              : 
    2283            0 :   if(object_id != object_id2) return false;
    2284              : 
    2285            0 :   const OksClass *pclass_id =
    2286            0 :     d->type == OksData::object_type  ? d->data.OBJECT->GetClass() :
    2287            0 :     d->type == OksData::uid_type     ? d->data.UID.class_id :
    2288            0 :     0;
    2289              : 
    2290            0 :   const OksClass *pclass_id2 =
    2291            0 :     d2->type == OksData::object_type ? d2->data.OBJECT->GetClass() :
    2292            0 :     d2->type == OksData::uid_type    ? d2->data.UID.class_id :
    2293            0 :     0;
    2294              : 
    2295            0 :   if(pclass_id && pclass_id2) return (pclass_id == pclass_id2 ? true : false);
    2296              : 
    2297            0 :   const std::string& class_id = 
    2298            0 :     (pclass_id ? pclass_id->get_name() : *(static_cast<std::string*>(d->data.UID2.class_id)));
    2299              : 
    2300            0 :   const std::string& class_id2 =
    2301            0 :     (pclass_id2 ? pclass_id2->get_name() : *(static_cast<std::string*>(d2->data.UID2.class_id)));
    2302              : 
    2303            0 :   return (class_id == class_id2 ? true : false);
    2304              : }
    2305              : 
    2306              : 
    2307              : void
    2308            0 : OksObject::AddRelationshipValue(const OksDataInfo *odi, OksObject *object)
    2309              : {
    2310            0 :   const OksRelationship * r = odi->relationship;
    2311            0 :   size_t offset = odi->offset;
    2312              : 
    2313            0 :   if(!object) {
    2314            0 :     throw oks::ObjectSetError(this, true, r->get_name(), "the (null) value is not allowed");
    2315              :   }
    2316              : 
    2317            0 :   check_class_type(r, object);
    2318              : 
    2319            0 :   if( r->get_high_cardinality_constraint() != OksRelationship::Many ) {
    2320            0 :     std::ostringstream text;
    2321            0 :     text << "cannot add value " << object << " since the relationship has no \'many\' high cardinality constraint";
    2322            0 :     throw oks::ObjectSetError(this, true, r->get_name(), text.str());
    2323            0 :   }
    2324              : 
    2325            0 :   try {
    2326            0 :     object->add_RCR(this, r);
    2327              :   }
    2328            0 :   catch(oks::exception& ex) {
    2329            0 :     throw oks::ObjectSetError(this, true, r->get_name(), ex);
    2330            0 :   }
    2331              : 
    2332            0 :   check_file_lock(0, r);
    2333              : 
    2334            0 :   data[offset].data.LIST->push_back(new OksData(object));
    2335              : 
    2336            0 :   notify();
    2337            0 : }
    2338              : 
    2339              : 
    2340              : void
    2341            0 : OksObject::AddRelationshipValue(const std::string& name, OksObject *object)
    2342              : {
    2343            0 :   OksDataInfo::Map::iterator i = uid.class_id->p_data_info->find(name);
    2344              : 
    2345            0 :   if(i == uid.class_id->p_data_info->end()) {
    2346            0 :     std::ostringstream text;
    2347            0 :     text << "object " << this << " has no relationship \"" << name << '\"';
    2348            0 :     throw oks::ObjectSetError(this, true, name, text.str());
    2349            0 :   }
    2350              : 
    2351            0 :   AddRelationshipValue(i->second, object);
    2352            0 : }
    2353              : 
    2354              : 
    2355              : void
    2356            0 : OksObject::RemoveRelationshipValue(const OksDataInfo *odi, OksObject *object)
    2357              : {
    2358            0 :   const OksRelationship * r = odi->relationship;
    2359              :   
    2360            0 :   if(!object) {
    2361            0 :     throw oks::ObjectSetError(this, true, r->get_name(), "cannot remove (null) object");
    2362              :   }
    2363              : 
    2364            0 :   if(r->get_high_cardinality_constraint() != OksRelationship::Many) {
    2365            0 :     std::ostringstream text;
    2366            0 :     text << "cannot remove object " << object << " since the relationship has no \'many\' high cardinality constraint";
    2367            0 :     throw oks::ObjectSetError(this, true, r->get_name(), text.str());
    2368            0 :   }
    2369              : 
    2370            0 :   OksData d(object);
    2371            0 :   size_t offset = odi->offset;
    2372            0 :   OksData::List * list = data[offset].data.LIST;
    2373              :   
    2374            0 :   for(OksData::List::iterator i = list->begin(); i != list->end();) {
    2375            0 :     OksData * d2 = *i;
    2376            0 :     if(cmp_data(d2, &d)) {
    2377            0 :       check_file_lock(0, r);
    2378            0 :       object->remove_RCR(this, r);
    2379            0 :       list->erase(i);
    2380            0 :       delete d2;
    2381            0 :       notify();
    2382            0 :       return;
    2383              :     }
    2384            0 :     else ++i;
    2385              :   }
    2386              : 
    2387            0 :   std::ostringstream text;
    2388            0 :   text << "cannot remove object " << object << " since the relationship\'s value has no such object";
    2389            0 :   throw oks::ObjectSetError(this, true, r->get_name(), text.str());
    2390            0 : }
    2391              : 
    2392              : 
    2393              : void
    2394            0 : OksObject::RemoveRelationshipValue(const std::string& name, OksObject * object)
    2395              : {
    2396            0 :   OksDataInfo::Map::iterator i = uid.class_id->p_data_info->find(name);
    2397              : 
    2398            0 :   if(i == uid.class_id->p_data_info->end()) {
    2399            0 :     std::ostringstream text;
    2400            0 :     text << "object " << this << " has no relationship \"" << name << '\"';
    2401            0 :     throw oks::ObjectSetError(this, true, name, text.str());
    2402            0 :   }
    2403              : 
    2404            0 :   RemoveRelationshipValue(i->second, object);
    2405            0 : }
    2406              : 
    2407              : void
    2408            0 : OksObject::SetRelationshipValue(const std::string& name, const std::string& class_id, const std::string& object_id)
    2409              : {
    2410            0 :   OksDataInfo::Map::iterator i = uid.class_id->p_data_info->find(name);
    2411              :   
    2412            0 :   if(i == uid.class_id->p_data_info->end()) {
    2413            0 :     std::ostringstream text;
    2414            0 :     text << "object " << this << " has no relationship \"" << name << '\"';
    2415            0 :     throw oks::ObjectSetError(this, true, name, text.str());
    2416            0 :   }
    2417              : 
    2418            0 :   OksDataInfo * odi = i->second;
    2419              : 
    2420              : 
    2421            0 :   const OksRelationship * r = odi->relationship;
    2422            0 :   size_t offset = odi->offset;
    2423              : 
    2424            0 :   if(object_id.empty() || class_id.empty()) {
    2425            0 :     check_non_null(r, 0);
    2426            0 :     check_file_lock(0, r);
    2427              : 
    2428            0 :     if( (data[offset].type == OksData::object_type) && data[offset].data.OBJECT ) {
    2429            0 :       data[offset].data.OBJECT->remove_RCR(this, r);
    2430              :     }
    2431              : 
    2432            0 :     data[offset].Set((OksObject *)0);
    2433              : 
    2434            0 :     notify();
    2435              : 
    2436            0 :     return;
    2437              :   }
    2438              :   
    2439            0 :   OksClass *c = uid.class_id->p_kernel->find_class(class_id);
    2440              : 
    2441            0 :   if(c) {
    2442            0 :     check_class_type(r, c);
    2443              :   }
    2444            0 :   else if(r->get_type() != class_id) {
    2445            0 :     std::ostringstream text;
    2446            0 :     text << "cannot set value \"" << object_id << '@' << class_id << "\" since the class \"" << class_id
    2447            0 :          << "\" is not defined and it is impossible to check its validity for given type of relationship";
    2448            0 :     throw oks::ObjectSetError(this, true, r->get_name(), text.str());
    2449            0 :   }
    2450              : 
    2451            0 :   if(r->get_high_cardinality_constraint() == OksRelationship::Many) {
    2452            0 :     std::ostringstream text;
    2453            0 :     text << "cannot set object \"" << object_id << '@' << class_id << "\" since the relationship has \'many\' high cardinality constraint";
    2454            0 :     throw oks::ObjectSetError(this, true, r->get_name(), text.str());
    2455            0 :   }
    2456              : 
    2457            0 :   OksObject *o = nullptr;
    2458              : 
    2459            0 :   if(c) o = c->get_object(object_id);
    2460              : 
    2461            0 :   if(o) {
    2462            0 :     SetRelationshipValue(name, o);
    2463              :   }
    2464              :   else {
    2465            0 :     check_file_lock(0, r);
    2466              : 
    2467            0 :     if( (data[offset].type == OksData::object_type) && data[offset].data.OBJECT ) {
    2468            0 :       data[offset].data.OBJECT->remove_RCR(this, r);
    2469              :     }
    2470              : 
    2471            0 :     data[offset].Set(class_id, object_id); 
    2472              : 
    2473            0 :     notify();
    2474              :   }
    2475              : }
    2476              : 
    2477              : 
    2478              : void
    2479            0 : OksObject::AddRelationshipValue(const std::string& name, const std::string& class_id, const std::string& object_id)
    2480              : {
    2481            0 :   OksDataInfo::Map::iterator i = uid.class_id->p_data_info->find(name);
    2482              :   
    2483            0 :   if(i == uid.class_id->p_data_info->end()) {
    2484            0 :     std::ostringstream text;
    2485            0 :     text << "object " << this << " has no relationship \"" << name << '\"';
    2486            0 :     throw oks::ObjectSetError(this, true, name, text.str());
    2487            0 :   }
    2488              : 
    2489            0 :   OksDataInfo * odi = i->second;
    2490            0 :   const OksRelationship * r = odi->relationship;
    2491              : 
    2492            0 :   if(class_id.empty() || object_id.empty()) {
    2493            0 :     throw oks::ObjectSetError(this, true, r->get_name(), "cannot add (null) object");
    2494              :   }
    2495              : 
    2496            0 :   const std::string& r_class_type = r->get_type();
    2497            0 :   size_t offset = odi->offset;
    2498              : 
    2499            0 :   OksClass *c = uid.class_id->p_kernel->find_class(class_id);
    2500              : 
    2501            0 :   if(c) {
    2502            0 :     check_class_type(r, c);
    2503              :   }
    2504            0 :   else if(r_class_type != class_id) {
    2505            0 :     std::ostringstream text;
    2506            0 :     text << "cannot add value \"" << object_id << '@' << class_id << "\" since the class \"" << class_id
    2507            0 :          << "\" is not defined and it is impossible to check its validity for given type of relationship";
    2508            0 :     throw oks::ObjectSetError(this, true, r->get_name(), text.str());
    2509            0 :   }
    2510              : 
    2511            0 :   if(r->get_high_cardinality_constraint() != OksRelationship::Many) {
    2512            0 :     std::ostringstream text;
    2513            0 :     text << "cannot add object \"" << object_id << '@' << class_id << "\" since the relationship has no \'many\' high cardinality constraint";
    2514            0 :     throw oks::ObjectSetError(this, true, r->get_name(), text.str());
    2515            0 :   }
    2516              : 
    2517            0 :   OksObject *o = nullptr;
    2518              : 
    2519            0 :   if(c) o = c->get_object(object_id);
    2520              : 
    2521            0 :   if(o) {
    2522            0 :     AddRelationshipValue(odi, o);
    2523              :   }
    2524              :   else {
    2525            0 :     check_file_lock(0, r);
    2526            0 :     data[offset].data.LIST->push_back(new OksData(class_id, object_id));
    2527            0 :     notify();
    2528              :   }
    2529            0 : }
    2530              : 
    2531              : 
    2532              : void
    2533            0 : OksObject::RemoveRelationshipValue(const std::string& name, const std::string& class_id, const std::string& object_id)
    2534              : {
    2535            0 :   OksDataInfo::Map::iterator i = uid.class_id->p_data_info->find(name);
    2536              : 
    2537            0 :   if(i == uid.class_id->p_data_info->end()) {
    2538            0 :     std::ostringstream text;
    2539            0 :     text << "object " << this << " has no relationship \"" << name << '\"';
    2540            0 :     throw oks::ObjectSetError(this, true, name, text.str());
    2541            0 :   }
    2542              : 
    2543            0 :   OksDataInfo * odi = i->second;
    2544            0 :   const OksRelationship * r = odi->relationship;
    2545              : 
    2546            0 :   if(object_id.empty() || class_id.empty()) {
    2547            0 :     throw oks::ObjectSetError(this, true, r->get_name(), "cannot remove (null) object");
    2548              :   }
    2549              : 
    2550            0 :   if( r->get_high_cardinality_constraint() != OksRelationship::Many ) {
    2551            0 :     std::ostringstream text;
    2552            0 :     text << "cannot remove object \"" << object_id << '@' << class_id << "\" since the relationship has no \'many\' high cardinality constraint";
    2553            0 :     throw oks::ObjectSetError(this, true, r->get_name(), text.str());
    2554            0 :   }
    2555              : 
    2556            0 :   size_t offset = odi->offset;
    2557            0 :   OksData d(class_id, object_id);
    2558            0 :   OksData::List * list = data[offset].data.LIST;
    2559              : 
    2560            0 :   for(OksData::List::iterator j = list->begin(); j != list->end();) {
    2561            0 :     OksData * d2 = *j;
    2562            0 :     if(cmp_data(d2, &d)) {
    2563            0 :       check_file_lock(0, r);
    2564            0 :       list->erase(j);
    2565            0 :       delete d2;
    2566            0 :       notify();
    2567            0 :       return;
    2568              :     }
    2569            0 :     else ++j;
    2570              :   }
    2571              : 
    2572            0 :   std::ostringstream text;
    2573            0 :   text << "cannot remove object \"" << object_id << '@' << class_id << "\" since the relationship\'s value has no such object";
    2574            0 :   throw oks::ObjectSetError(this, true, r->get_name(), text.str());
    2575            0 : }
    2576              : 
    2577              : 
    2578              : void
    2579         6368 : OksObject::add_RCR(OksObject *o, const OksRelationship *r)
    2580              : {
    2581         6368 :   if(r->get_is_composite() == false) return;
    2582              : 
    2583         2827 :   TLOG_DEBUG(4) << "object " << this << " adds RCR to object " << o << " throught relationship \"" << r->get_name() << '\"';
    2584              : 
    2585         2827 :   if(!p_rcr) {
    2586         1892 :     p_rcr = new std::list<OksRCR *>();
    2587              :   }
    2588              :   else {
    2589         3286 :     for(const auto& i : *p_rcr) {
    2590         2351 :       if(i->relationship == r) {
    2591         2180 :         if(i->obj == o) {
    2592            0 :           TLOG_DEBUG(4) << "[this=" << this << ", o=" << o << ", r=\"" << r->get_name() << "\"]: such RCR was already set.";
    2593            0 :           return;
    2594              :           }
    2595         2180 :         else if(r->get_is_exclusive()) {
    2596            0 :           throw oks::AddRcrError(this, r->get_name(), o, i->obj);
    2597              :         }
    2598              :       }
    2599              :     }
    2600              :   }
    2601              : 
    2602         2827 :   p_rcr->push_back(new OksRCR(o, r));
    2603              : }
    2604              : 
    2605              : 
    2606              : void
    2607            1 : OksObject::remove_RCR(OksObject *o, const OksRelationship *r) noexcept
    2608              : {
    2609            1 :   TLOG_DEBUG(4) << "object " << this << " removes RCR from object " << o << " through " << r->get_name();
    2610              : 
    2611            1 :   if(r->get_is_composite() == false || !p_rcr) return;
    2612              : 
    2613            1 :   for(std::list<OksRCR *>::iterator i = p_rcr->begin(); i != p_rcr->end(); ++i) {
    2614            1 :     OksRCR *rcr = *i;
    2615              : 
    2616            1 :     if(rcr->obj == o && rcr->relationship == r) {
    2617            1 :       p_rcr->erase(i);
    2618            1 :       delete rcr;
    2619              :       break;
    2620              :     }
    2621              :   }
    2622              : 
    2623            1 :   if(p_rcr->empty()) {
    2624            1 :     delete p_rcr;
    2625            1 :     p_rcr = nullptr;
    2626              :   }
    2627              : }
    2628              : 
    2629              : 
    2630              : void
    2631         3560 : OksObject::bind(OksData *d, const BindInfo& info)
    2632              : {
    2633         3560 :   const OksClass * c(0);
    2634         3560 :   OksObject * o(0);
    2635              : 
    2636         3560 :   if(d->type == OksData::uid_type) {
    2637         3560 :     if(d->data.UID.class_id && !d->data.UID.object_id->empty()) {
    2638         3560 :       c = d->data.UID.class_id;
    2639         3560 :       o = c->get_object(d->data.UID.object_id);
    2640              :     }
    2641              :     else {
    2642            0 :       d->Set((OksObject *)0);
    2643            0 :       return;
    2644              :     }
    2645              :   }
    2646              :   else {
    2647            0 :     if(d->data.UID2.class_id->empty() || d->data.UID2.object_id->empty()) {
    2648            0 :       d->Set((OksObject *)0);
    2649            0 :       return;
    2650              :     }
    2651              : 
    2652            0 :     c = info.k->find_class(*d->data.UID2.class_id);
    2653              : 
    2654            0 :     if(c) {
    2655            0 :       o = c->get_object(d->data.UID2.object_id);
    2656              :     }
    2657              :     else {
    2658            0 :       std::ostringstream text;
    2659            0 :       text << "Class of object " << *d << " is not defined";
    2660            0 :       throw oks::ObjectBindError(info.o, d, info.r, true, text.str(), "");
    2661            0 :     }
    2662              :   }
    2663              : 
    2664         3560 :   if(c != info.r->p_class_type) {
    2665         1141 :     if(const OksClass::FList * slist = c->all_super_classes()) {
    2666         1639 :       for(OksClass::FList::const_iterator i = slist->begin(); i != slist->end(); ++i) {
    2667         1639 :         if(info.r->p_class_type == *i) {
    2668         1141 :           goto check_passed;
    2669              :         }
    2670              :       }
    2671              :     }
    2672              : 
    2673            0 :     {
    2674            0 :       std::ostringstream text;
    2675            0 :       text << "The relationship has class type \"" << info.r->get_type() << "\" and the referenced object " << *d << " is not of that class or derived one";
    2676            0 :       throw oks::ObjectBindError(info.o, d, info.r, true, text.str(), "");
    2677            0 :     }
    2678              : 
    2679         1141 :     check_passed: ;
    2680              :   }
    2681              : 
    2682         3560 :   if(!o) {
    2683            0 :     std::ostringstream text;
    2684            0 :     text << "Cannot find object " << *d;
    2685            0 :     throw oks::ObjectBindError(info.o, d, info.r, false, text.str(), "");
    2686            0 :   }
    2687              : 
    2688         3560 :   try {
    2689         3560 :     d->Set(o);
    2690         3560 :     o->add_RCR(info.o, info.r);
    2691              :   }
    2692            0 :   catch(oks::exception& ex) {
    2693            0 :     std::ostringstream text;
    2694            0 :     text << "Failed to set relationship value " << o;
    2695            0 :     throw oks::ObjectBindError(info.o, d, info.r, true, text.str(), ex);
    2696            0 :   }
    2697              : }
    2698              : 
    2699              : 
    2700              : struct BindWarning {
    2701              :   std::ostringstream * p_warnings; // in 99.999% cases there is no need to call the constructor
    2702              :   unsigned int p_warnings_count;
    2703              : 
    2704         2350 :   BindWarning() : p_warnings(0), p_warnings_count(0) { ; }
    2705         2350 :   ~BindWarning() { delete p_warnings; }
    2706              : 
    2707              :   void add(const OksData& d, const OksRelationship * r);
    2708              :   void add(const OksRelationship * r);
    2709              :   std::ostringstream& add();
    2710              : };
    2711              : 
    2712              : std::ostringstream&
    2713           18 : BindWarning::add()
    2714              : {
    2715           18 :   if(p_warnings_count != 0) (*p_warnings) << '\n';
    2716           18 :   else {p_warnings = new std::ostringstream();}
    2717           18 :   p_warnings_count++;
    2718           18 :   return *p_warnings;
    2719              : }
    2720              : 
    2721              : 
    2722              : void
    2723            0 : BindWarning::add(const OksData& d, const OksRelationship * r)
    2724              : {
    2725            0 :   add() << " * object " << d << " via relationship \"" << r->get_name() << '\"';
    2726            0 : }
    2727              : 
    2728              : void
    2729           18 : BindWarning::add(const OksRelationship * r)
    2730              : {
    2731           18 :   add() << " * relationship \"" << r->get_name() << "\" has non-zero low cardinality constraint, but it is empty";
    2732           18 : }
    2733              : 
    2734              : 
    2735              : void
    2736         4948 : OksObject::bind_objects()
    2737              : {
    2738         4948 :   const OksClass * c = uid.class_id;
    2739              : 
    2740         4948 :   if(c->p_all_relationships->empty()) return;  // if class has no relationships then nothing to do
    2741              : 
    2742         2350 :   BindInfo info;
    2743         2350 :   info.k = c->p_kernel;
    2744         2350 :   info.o = this;
    2745              : 
    2746         2350 :   OksData * d(data + c->number_of_all_attributes());
    2747              : 
    2748         2350 :   BindWarning warnings;
    2749              : 
    2750         9150 :   for (auto & i : (*c->p_all_relationships))
    2751              :     {
    2752         6800 :       info.r = i;
    2753              : 
    2754         6800 :       if(d->type == OksData::object_type)
    2755              :         {
    2756         1065 :           if (i->get_low_cardinality_constraint() == OksRelationship::One && d->data.OBJECT == nullptr) { warnings.add(i); }
    2757              :         }
    2758         5735 :       else if (d->type == OksData::uid_type)
    2759              :         {
    2760         1819 :           if (d->data.UID.object_id == nullptr)
    2761              :             {
    2762            0 :               if (i->get_low_cardinality_constraint() == OksRelationship::One) { warnings.add(i); }
    2763              :             }
    2764              : 
    2765         1819 :           try
    2766              :             {
    2767         1819 :               bind(d, info);
    2768              :             }
    2769            0 :           catch (oks::ObjectBindError& ex)
    2770              :             {
    2771            0 :               if (ex.p_is_error) { throw; }
    2772            0 :               else { warnings.add(*ex.p_data, ex.p_rel); }
    2773            0 :             }
    2774              :         }
    2775         3916 :       else if (d->type == OksData::list_type)
    2776              :         {
    2777         3916 :           if (d->data.LIST->empty())
    2778              :             {
    2779         2110 :               if (i->get_low_cardinality_constraint() == OksRelationship::One) { warnings.add(i); }
    2780              :             }
    2781              :           else
    2782              :             {
    2783         5585 :               for (const auto& i2 : *d->data.LIST)
    2784              :                 {
    2785         3779 :                   if (i2->type == OksData::uid_type || i2->type == OksData::uid2_type)
    2786              :                     {
    2787         1741 :                       try
    2788              :                         {
    2789         1741 :                           bind(i2, info);
    2790              :                         }
    2791            0 :                       catch (oks::ObjectBindError& ex)
    2792              :                         {
    2793            0 :                           if (ex.p_is_error) { throw; }
    2794            0 :                           else { warnings.add(*ex.p_data, ex.p_rel); }
    2795            0 :                         }
    2796              :                     }
    2797              :                 }
    2798              :             }
    2799              :         }
    2800            0 :       else if (d->type == OksData::uid2_type)
    2801              :         {
    2802            0 :           if (i->get_low_cardinality_constraint() == OksRelationship::One && d->data.UID2.object_id == nullptr) { warnings.add(i); }
    2803            0 :           try
    2804              :             {
    2805            0 :               bind(d, info);
    2806              :             }
    2807            0 :           catch (oks::ObjectBindError& ex)
    2808              :             {
    2809            0 :               if (ex.p_is_error) { throw; }
    2810            0 :               else { warnings.add(*ex.p_data, ex.p_rel); }
    2811            0 :             }
    2812              :         }
    2813              : 
    2814         6800 :       d++;
    2815              :     }
    2816              : 
    2817         2350 :   if(warnings.p_warnings_count)
    2818              :     {
    2819           18 :       std::ostringstream text;
    2820           18 :       const char * s1(warnings.p_warnings_count == 1 ? "is" : "are");
    2821           18 :       const char * s2(warnings.p_warnings_count == 1 ? "" : "s");
    2822           18 :       text << "There " << s1 << " " << warnings.p_warnings_count << " unresolved reference" << s2 << " from object " << this
    2823           18 :            << '\n' << warnings.p_warnings->str();
    2824           18 :       throw oks::ObjectBindError(this, 0, 0, false, text.str(), "");
    2825           18 :     }
    2826         2350 : }
    2827              : 
    2828              : 
    2829              : void
    2830            0 : OksObject::unbind_file(const OksFile * f)
    2831              : {
    2832            0 :   const size_t num_of_attrs (uid.class_id->number_of_all_attributes());
    2833            0 :   const size_t num_of_rels  (uid.class_id->number_of_all_relationships());
    2834              : 
    2835            0 :   bool verbose = GetClass()->get_kernel()->get_verbose_mode();
    2836              : 
    2837            0 :   for(size_t i = 0; i < num_of_rels; ++i) {
    2838            0 :     OksData *d = &data[i + num_of_attrs];
    2839              :     
    2840            0 :     if(d) {
    2841            0 :       if(d->type == OksData::object_type) {
    2842            0 :         OksObject * o = d->data.OBJECT;
    2843              : 
    2844            0 :         if(o && o->file == f) {
    2845            0 :           if(verbose)
    2846            0 :             std::cout << "- unbind_file(\'" << f->get_full_file_name() << "\') in " << this << ": replace " << *d;
    2847              : 
    2848            0 :           d->Set(o->GetClass(), o->GetId());
    2849              : 
    2850            0 :           if(verbose) std::cout << " by " << *d << std::endl;
    2851              :         }
    2852              :       }
    2853            0 :       else if(d->type == OksData::list_type && d->data.LIST) {
    2854            0 :         for(const auto& j : *d->data.LIST) {
    2855            0 :           if(j && j->type == OksData::object_type) {
    2856            0 :             OksObject * o = j->data.OBJECT;
    2857              : 
    2858            0 :             if(o && o->file == f) {
    2859            0 :               if(verbose)
    2860            0 :                 std::cout << "+ unbind_file(\'" << f->get_full_file_name() << "\') in " << this << ": replace " << *j;
    2861              : 
    2862            0 :               j->Set(o->GetClass(), o->GetId());
    2863              : 
    2864            0 :               if(verbose) std::cout << " by " << *j << std::endl;
    2865              :             }
    2866              :           }
    2867              :         }
    2868              :       }
    2869              :     }
    2870              :   }
    2871            0 : }
    2872              : 
    2873              : OksObject::notify_obj
    2874            0 : OksObject::get_change_notify() const
    2875              : {
    2876            0 :   return uid.class_id->p_kernel->p_change_object_notify_fn;
    2877              : }
    2878              : 
    2879              :  /**
    2880              :   *  Invoke create object notification.
    2881              :   */
    2882              : 
    2883              : void
    2884           27 : OksObject::create_notify()
    2885              : {
    2886           27 :   OksKernel * k = uid.class_id->p_kernel;
    2887              : 
    2888           27 :   if(k->p_create_object_notify_fn) {
    2889            0 :     (*k->p_create_object_notify_fn)(this, k->p_create_object_notify_param);
    2890              :   }
    2891           27 : }
    2892              : 
    2893              :  /**
    2894              :   *  Invoke change object notification.
    2895              :   */
    2896              : 
    2897              : void
    2898           21 : OksObject::change_notify()
    2899              : {
    2900           21 :   OksKernel * k = uid.class_id->p_kernel;
    2901           21 :   if(k->p_change_object_notify_fn) {
    2902            0 :     (*k->p_change_object_notify_fn)(this, k->p_change_object_notify_param);
    2903              :   }
    2904           21 : }
    2905              : 
    2906              :  /**
    2907              :   *  Invoke delete object notification.
    2908              :   */
    2909              : 
    2910              : void
    2911         4975 : OksObject::delete_notify()
    2912              : {
    2913         4975 :   OksKernel * k = uid.class_id->p_kernel;
    2914              : 
    2915         4975 :   if(k->p_delete_object_notify_fn) {
    2916            0 :     (*k->p_delete_object_notify_fn)(this, k->p_delete_object_notify_param);
    2917              :   }
    2918         4975 : }
    2919              : 
    2920              : bool
    2921            0 : OksObject::check_links_and_report(const OksObject * o2, const std::set<OksFile *>& includes, const std::string& name, const char * msg) const
    2922              : {
    2923            0 :   if(get_file() != o2->get_file()) {
    2924            0 :     if(includes.find(o2->get_file()) == includes.end()) {
    2925            0 :       if(GetClass()->get_kernel()->get_silence_mode() == false) {
    2926            0 :         std::cerr <<
    2927              :           msg << ": no files inclusion path between referenced objects:\n"
    2928            0 :           "  object " << this << " from file \"" << get_file()->get_full_file_name() << "\" via relationship \"" << name << "\"\n"
    2929            0 :           "  has reference to object " << o2 << " from file \"" << o2->get_file()->get_full_file_name() << "\";\n"
    2930            0 :           "  file \"" << get_file()->get_full_file_name() << "\" includes " << includes.size() << " files:\n";
    2931              : 
    2932            0 :         for(std::set<OksFile *>::const_iterator j = includes.begin(); j != includes.end(); ++j) {
    2933            0 :           std::cerr << "  * \'" << (*j)->get_full_file_name() << "\'\n"; 
    2934              :         } 
    2935              :         
    2936              :       }
    2937              : 
    2938            0 :       return false;
    2939              :     }
    2940              :   }
    2941              :   
    2942              :   return true;
    2943              : }
    2944              : 
    2945              : static void
    2946            0 : test_dangling_references(const OksObject * obj, const OksData& d, const OksRelationship& r, std::string& result)
    2947              : {
    2948            0 :   if(d.type == OksData::uid_type || d.type == OksData::uid2_type) {
    2949            0 :     std::ostringstream text;
    2950              : 
    2951            0 :     if(result.empty()) {
    2952            0 :       text << " * object " << obj << " has dangling references:\n";
    2953              :     }
    2954              : 
    2955            0 :     text << "  - object " << d << " referenced via \'" << r.get_name() << "\'\n";
    2956              : 
    2957            0 :     result += text.str();
    2958            0 :   }
    2959            0 : }
    2960              : 
    2961              : std::string
    2962            0 : OksObject::report_dangling_references() const
    2963              : {
    2964            0 :   unsigned short l1 = GetClass()->number_of_all_attributes();
    2965            0 :   unsigned short l2 = l1 + GetClass()->number_of_all_relationships();
    2966              : 
    2967            0 :   std::list<OksRelationship *>::const_iterator ri = GetClass()->all_relationships()->begin();
    2968              : 
    2969            0 :   std::string result;
    2970              : 
    2971            0 :   while(l1 < l2) {
    2972            0 :     OksDataInfo odi(l1, *ri);
    2973            0 :     OksData * d(GetRelationshipValue(&odi));
    2974              : 
    2975            0 :     if(d->type == OksData::list_type) {
    2976            0 :       for(OksData::List::const_iterator li = d->data.LIST->begin(); li != d->data.LIST->end(); ++li) {
    2977            0 :         test_dangling_references(this, **li, **ri, result);
    2978              :       }
    2979              :     }
    2980              :     else {
    2981            0 :       test_dangling_references(this, *d, **ri, result);
    2982              :     }
    2983              : 
    2984            0 :     ++l1;
    2985            0 :     ++ri;
    2986              :   }
    2987              : 
    2988            0 :   return result;
    2989            0 : }
    2990              : 
    2991              : 
    2992              : bool
    2993            0 : OksObject::is_consistent(const std::set<OksFile *>& includes, const char * msg) const
    2994              : {
    2995            0 :   unsigned short l1 = GetClass()->number_of_all_attributes();
    2996            0 :   unsigned short l2 = l1 + GetClass()->number_of_all_relationships();
    2997              : 
    2998            0 :   std::list<OksRelationship *>::const_iterator ri = GetClass()->all_relationships()->begin();
    2999              : 
    3000            0 :   bool return_value = true;
    3001              : 
    3002            0 :   while(l1 < l2) {
    3003            0 :     OksDataInfo odi(l1, *ri);
    3004            0 :     OksData * d(GetRelationshipValue(&odi));
    3005              : 
    3006            0 :     if(d->type == OksData::object_type) {
    3007            0 :       if(OksObject * o2 = d->data.OBJECT) {
    3008            0 :         check_links_and_report(o2, includes, odi.relationship->get_name(), msg);
    3009              :       }
    3010              :     }
    3011            0 :     else if(d->type == OksData::list_type) {
    3012            0 :       for(OksData::List::const_iterator li = d->data.LIST->begin(); li != d->data.LIST->end(); ++li) {
    3013            0 :         if((*li)->type == OksData::object_type) {
    3014            0 :           if(OksObject * o2 = (*li)->data.OBJECT) {
    3015            0 :             check_links_and_report(o2, includes, odi.relationship->get_name(), msg);
    3016              :           }
    3017              :         }
    3018              :       }
    3019              :     }
    3020              : 
    3021            0 :     if(d->IsConsistent(odi.relationship, this, "ERROR") == false) {
    3022            0 :       return_value = false;
    3023              :     }
    3024              : 
    3025            0 :     ++l1;
    3026            0 :     ++ri;
    3027              :   }
    3028              : 
    3029              :     // check inclusion of the schema file
    3030              : 
    3031            0 :   if(includes.find(GetClass()->get_file()) == includes.end()) {
    3032            0 :     if(GetClass()->get_kernel()->get_silence_mode() == false) {
    3033            0 :       std::cerr <<
    3034            0 :         "ERROR: the schema file is not included for object " << this << ".\n"
    3035            0 :         "  The data file \"" << get_file()->get_full_file_name() << "\" containg above object\n"
    3036              :         "  cannot be loaded or edited without inclusion of additional files.\n"
    3037              :         "  The file shall explicitly or implicitly (i.e. via other included files) include schema file\n"
    3038            0 :         "  \"" << GetClass()->get_file()->get_full_file_name() << "\" defining class \"" << GetClass()->get_name() << "\".\n";
    3039              :     }
    3040              : 
    3041            0 :     return false;
    3042              :   }
    3043              : 
    3044              : 
    3045              :   return return_value;
    3046              : }
    3047              : 
    3048              : 
    3049              : struct RefData {
    3050              :   OksObject::FSet& refs;
    3051              :   OksObject::FSet tested_objs;
    3052              :   oks::ClassSet * classes;
    3053              : 
    3054            0 :   RefData(OksObject::FSet& r, oks::ClassSet * cs) : refs(r), classes(cs) {
    3055            0 :     if(classes && classes->empty()) classes = nullptr;
    3056            0 :   }
    3057              : };
    3058              : 
    3059              : 
    3060              : static void _references(const OksObject * obj, unsigned long recursion_depth, RefData& data);
    3061              : 
    3062              : 
    3063              : static void
    3064            0 : insert2refs(const OksObject *o, unsigned long recursion_depth, RefData& data)
    3065              : {
    3066            0 :   recursion_depth--;
    3067              : 
    3068            0 :   std::pair<OksObject::FSet::iterator,bool> ret = data.tested_objs.insert(const_cast<OksObject *>(o));
    3069              : 
    3070            0 :   if(ret.second==true) {
    3071            0 :     if(data.classes == nullptr || data.classes->find(const_cast<OksClass *>(o->GetClass())) != data.classes->end()) {
    3072            0 :       data.refs.insert(const_cast<OksObject *>(o));
    3073              :     }
    3074            0 :     o->SetTransientData((void *)recursion_depth);
    3075            0 :     _references(o, recursion_depth, data);
    3076              :   }
    3077            0 :   else if(o->GetTransientData() < (void *)recursion_depth) {
    3078            0 :     o->SetTransientData((void *)recursion_depth);
    3079            0 :     _references(o, recursion_depth, data);
    3080              :   }
    3081            0 : }
    3082              : 
    3083              : static void
    3084            0 : _references(const OksObject * obj, unsigned long recursion_depth, RefData& data)
    3085              : {
    3086            0 :   if(recursion_depth == 0) return;
    3087              : 
    3088            0 :   size_t l1 = obj->GetClass()->number_of_all_attributes();
    3089            0 :   size_t l2 = l1 + obj->GetClass()->number_of_all_relationships();
    3090              : 
    3091            0 :   while(l1 < l2) {
    3092            0 :     OksDataInfo odi(l1, (OksRelationship *)0);
    3093            0 :     OksData * d(obj->GetRelationshipValue(&odi));
    3094              : 
    3095            0 :     if(d->type == OksData::object_type) {
    3096            0 :       if(OksObject * o2 = d->data.OBJECT) {
    3097            0 :         insert2refs(o2, recursion_depth, data);
    3098              :       }
    3099              :     }
    3100            0 :     else if(d->type == OksData::list_type) {
    3101            0 :       for(OksData::List::const_iterator li = d->data.LIST->begin(); li != d->data.LIST->end(); ++li) {
    3102            0 :         if((*li)->type == OksData::object_type) {
    3103            0 :           if(OksObject * o2 = (*li)->data.OBJECT) {
    3104            0 :             insert2refs(o2, recursion_depth, data);
    3105              :           }
    3106              :         }
    3107              :       }
    3108              :     }
    3109              : 
    3110            0 :     ++l1;
    3111              :   }
    3112              : }
    3113              : 
    3114              : void
    3115            0 : OksObject::references(OksObject::FSet& refs, unsigned long recursion_depth, bool add_self, oks::ClassSet * classes) const
    3116              : {
    3117            0 :   struct RefData data(refs, classes);
    3118              : 
    3119            0 :   std::lock_guard lock(uid.class_id->p_kernel->p_objects_refs_mutex);
    3120              : 
    3121            0 :   OksObject::FSet tested_objs;
    3122              : 
    3123            0 :   if(add_self) {
    3124            0 :     insert2refs(this, recursion_depth + 1, data);
    3125              :   }
    3126              : 
    3127            0 :   _references(this, recursion_depth, data);
    3128              : 
    3129            0 :   TLOG_DEBUG(2) <<  "OksObject::references(" << this << ", " << recursion_depth << ") returns " << refs.size() << " objects";
    3130            0 : }
    3131              : 
    3132              : 
    3133              : OksObject::FList *
    3134            0 : OksObject::get_all_rels(const std::string& name) const
    3135              : {
    3136            0 :   bool any_name = (name == "*");
    3137              :   
    3138            0 :   OksObject::FList * result = nullptr;
    3139              :   
    3140            0 :   const OksClass::Map& all_classes(GetClass()->get_kernel()->classes());
    3141              : 
    3142            0 :   for(OksClass::Map::const_iterator i = all_classes.begin(); i != all_classes.end(); ++i) {
    3143            0 :     OksClass * c(i->second);
    3144            0 :     if(any_name || c->find_relationship(name) != nullptr) {
    3145            0 :       if(const OksObject::Map * objs = i->second->objects()) {
    3146            0 :         for(OksObject::Map::const_iterator j = objs->begin(); j != objs->end(); ++j) {
    3147            0 :           OksObject *o(j->second);
    3148            0 :           unsigned short l1, l2;
    3149              :           
    3150            0 :           if(any_name) {
    3151            0 :             l1 = c->number_of_all_attributes();
    3152            0 :             l2 = l1 + c->number_of_all_relationships();
    3153              :           }
    3154              :           else {
    3155            0 :             l1 = c->data_info(name)->offset;
    3156            0 :             l2 = l1+1;
    3157              :           }
    3158              : 
    3159            0 :           while(l1 < l2) {
    3160            0 :             OksDataInfo odi(l1, (OksRelationship *)0);
    3161            0 :             OksData * d(o->GetRelationshipValue(&odi));
    3162              : 
    3163            0 :             if(d->type == OksData::object_type) {
    3164            0 :               if(this == d->data.OBJECT) {
    3165            0 :                 if(!result) result = new OksObject::FList();
    3166            0 :                 result->push_back(o);
    3167            0 :                 break;
    3168              :               }
    3169              :             }
    3170            0 :             else if(d->type == OksData::list_type) {
    3171            0 :               bool found = false;
    3172            0 :               for(OksData::List::const_iterator li = d->data.LIST->begin(); li != d->data.LIST->end(); ++li) {
    3173            0 :                 OksData * lid(*li);
    3174            0 :                 if(lid->type == OksData::object_type) {
    3175            0 :                   if(this == lid->data.OBJECT) {
    3176            0 :                     if(!result) result = new OksObject::FList();
    3177            0 :                     result->push_back(o);
    3178              :                     found = true;
    3179              :                     break; // exit given relationship value iterator
    3180              :                   }
    3181              :                 }
    3182              :               }
    3183            0 :               if(found) break; //exit relationships iterator for given object
    3184              :             }
    3185            0 :             ++l1;
    3186              :           }
    3187              :         }
    3188              :       }
    3189              :     }
    3190              :   }
    3191              :   
    3192            0 :   return result;
    3193              : }
    3194              : 
    3195              : void
    3196           21 : OksObject::check_file_lock(const OksAttribute * a, const OksRelationship * r)
    3197              : {
    3198           21 :   try {
    3199           21 :     file->lock();
    3200              :   }
    3201            0 :   catch(oks::exception& ex) {
    3202            0 :     throw oks::ObjectSetError(this, (a == nullptr), (a ? a->get_name() : r->get_name()), ex);
    3203            0 :   }
    3204           21 : }
    3205              : 
    3206              : } // namespace oks
    3207              : } // namespace dunedaq
        

Generated by: LCOV version 2.0-1