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