LCOV - code coverage report
Current view: top level - oks/src - object.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 27.3 % 1840 502
Test Date: 2026-10-11 15:05:20 Functions: 44.4 % 90 40

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

Generated by: LCOV version 2.0-1