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