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