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