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: 2025-12-21 13:07:08 Functions: 43.3 % 90 39

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

Generated by: LCOV version 2.0-1