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