Line data Source code
1 : /* File: io_import_elements.c; Copyright and License: see below */
2 :
3 : #include "io_import_elements.h"
4 : #include "u8/u8_error.h"
5 : #include "utf8stringbuf/utf8string.h"
6 : #include "u8/u8_trace.h"
7 : #include <assert.h>
8 : #include <gtk/gtk.h>
9 : #include <stdbool.h>
10 :
11 18 : void io_import_elements_init( io_import_elements_t *this_,
12 : data_database_reader_t *db_reader,
13 : ctrl_controller_t *controller,
14 : data_stat_t *io_stat,
15 : utf8stream_writer_t *out_english_report )
16 : {
17 18 : U8_TRACE_BEGIN();
18 18 : assert( NULL != db_reader );
19 18 : assert( NULL != controller );
20 18 : assert( NULL != io_stat );
21 18 : assert( NULL != out_english_report );
22 :
23 18 : (*this_).db_reader = db_reader;
24 18 : (*this_).controller = controller;
25 18 : (*this_).stat = io_stat;
26 18 : (*this_).english_report = out_english_report;
27 :
28 18 : (*this_).mode = IO_IMPORT_MODE_CHECK;
29 18 : (*this_).paste_to_diagram = DATA_ROW_ID_VOID;
30 :
31 18 : ctrl_multi_step_changer_init( &((*this_).multi_step_changer), controller, db_reader );
32 :
33 18 : data_rules_init ( &((*this_).data_rules) );
34 :
35 : /* get the id of the root diagram if there is one */
36 18 : (*this_).root_diagram = DATA_ROW_ID_VOID;
37 : {
38 : data_small_set_t roots;
39 18 : data_small_set_init( &roots );
40 : const u8_error_t read_error
41 18 : = data_database_reader_get_diagram_ids_by_parent_id( (*this_).db_reader,
42 : DATA_ROW_ID_VOID,
43 : &roots
44 : );
45 18 : if ( read_error != U8_ERROR_NONE )
46 : {
47 0 : U8_LOG_ERROR_HEX( "error at reading root", read_error );
48 : }
49 : else
50 : {
51 18 : if ( 1 <= data_small_set_get_count( &roots ) )
52 : {
53 15 : data_id_t first_root = data_small_set_get_id( &roots, 0 );
54 15 : (*this_).root_diagram = data_id_get_row_id( &first_root );
55 : }
56 : else
57 : {
58 3 : U8_LOG_EVENT( "importing to empty database" );
59 : }
60 : }
61 18 : data_small_set_destroy( &roots );
62 : }
63 :
64 18 : U8_TRACE_END();
65 18 : }
66 :
67 15 : void io_import_elements_init_for_paste( io_import_elements_t *this_,
68 : data_row_id_t paste_to_diagram,
69 : data_database_reader_t *db_reader,
70 : ctrl_controller_t *controller,
71 : data_stat_t *io_stat,
72 : utf8stream_writer_t *out_english_report )
73 : {
74 15 : U8_TRACE_BEGIN();
75 :
76 15 : io_import_elements_init( this_, db_reader, controller, io_stat, out_english_report );
77 15 : (*this_).mode = IO_IMPORT_MODE_PASTE;
78 :
79 : /* check if diagram id exists */
80 : {
81 15 : data_diagram_init_empty( &((*this_).temp_diagram) );
82 : const u8_error_t read_error1
83 15 : = data_database_reader_get_diagram_by_id( (*this_).db_reader,
84 : paste_to_diagram,
85 : &((*this_).temp_diagram)
86 : );
87 15 : if ( U8_ERROR_NONE != read_error1 )
88 : {
89 1 : U8_LOG_ERROR_INT( "diagram id where to import json data to does not exist (anymore)", (*this_).paste_to_diagram );
90 1 : (*this_).paste_to_diagram = DATA_ROW_ID_VOID;
91 : }
92 : else
93 : {
94 14 : (*this_).paste_to_diagram = paste_to_diagram;
95 : }
96 15 : data_diagram_destroy( &((*this_).temp_diagram) );
97 : }
98 :
99 15 : U8_TRACE_END();
100 15 : }
101 :
102 18 : void io_import_elements_destroy( io_import_elements_t *this_ )
103 : {
104 18 : U8_TRACE_BEGIN();
105 18 : assert( NULL != (*this_).db_reader );
106 18 : assert( NULL != (*this_).controller );
107 18 : assert( NULL != (*this_).stat );
108 18 : assert( NULL != (*this_).english_report );
109 :
110 18 : data_rules_destroy ( &((*this_).data_rules) );
111 :
112 18 : ctrl_multi_step_changer_destroy( &((*this_).multi_step_changer) );
113 :
114 18 : (*this_).db_reader = NULL;
115 18 : (*this_).controller = NULL;
116 18 : (*this_).stat = NULL;
117 18 : (*this_).english_report = NULL;
118 :
119 18 : U8_TRACE_END();
120 18 : }
121 :
122 4 : void io_import_elements_set_mode( io_import_elements_t *this_, io_import_mode_t mode )
123 : {
124 4 : U8_TRACE_BEGIN();
125 :
126 4 : (*this_).mode = mode;
127 :
128 4 : U8_TRACE_END();
129 4 : }
130 :
131 8 : u8_error_t io_import_elements_sync_diagram( io_import_elements_t *this_,
132 : const data_diagram_t *diagram_ptr,
133 : const char *parent_uuid )
134 : {
135 8 : U8_TRACE_BEGIN();
136 8 : assert( NULL != diagram_ptr );
137 : /* parent_uuid is NULL if root diagram */
138 8 : u8_error_t sync_error = U8_ERROR_NONE;
139 :
140 : /* ANY MODE: determine parent id */
141 8 : data_row_id_t parent_row_id = (*this_).root_diagram;
142 8 : const bool parent_uuid_specified
143 8 : = (( parent_uuid != NULL )&&( ! utf8string_equals_str( parent_uuid, "" )));
144 8 : if ( parent_uuid_specified )
145 : {
146 0 : data_diagram_init_empty( &((*this_).temp_diagram ) );
147 : const u8_error_t read_error1
148 0 : = data_database_reader_get_diagram_by_uuid( (*this_).db_reader,
149 : parent_uuid,
150 : &((*this_).temp_diagram)
151 : );
152 0 : if ( read_error1 == U8_ERROR_NOT_FOUND )
153 : {
154 0 : U8_TRACE_INFO_STR( "no parent found, uuid:", parent_uuid );
155 : }
156 0 : else if ( read_error1 != U8_ERROR_NONE )
157 : {
158 0 : U8_TRACE_INFO_STR( "error at searching for parent diagram:", parent_uuid );
159 : }
160 : else
161 : {
162 0 : parent_row_id = data_diagram_get_row_id( &((*this_).temp_diagram ) );
163 : }
164 0 : data_diagram_destroy( &((*this_).temp_diagram ) );
165 : }
166 :
167 : /* update default parent diagram id */
168 8 : if (( (*this_).mode == IO_IMPORT_MODE_PASTE )&&( sync_error == U8_ERROR_NONE ))
169 : {
170 5 : if ( (*this_).paste_to_diagram == DATA_ROW_ID_VOID )
171 : {
172 0 : data_row_id_t parent_row_id = (*this_).root_diagram;
173 0 : U8_TRACE_INFO_INT( "in paste-clipboard mode, missing parent diagram set to", parent_row_id );
174 : }
175 : else
176 : {
177 : /* overwrite the parent diagram id, do not keep the one provided via the clipboard */
178 5 : parent_row_id = (*this_).paste_to_diagram;
179 : }
180 : }
181 :
182 : /* if PASTE */
183 8 : if (( (*this_).mode == IO_IMPORT_MODE_PASTE )&&( sync_error == U8_ERROR_NONE ))
184 : {
185 5 : data_diagram_copy( &((*this_).temp_diagram), diagram_ptr );
186 5 : data_diagram_set_parent_row_id( &((*this_).temp_diagram), parent_row_id );
187 : /* create new uuid for diagram if paste */
188 : {
189 : data_uuid_t new_uuid;
190 5 : data_uuid_init_new( &new_uuid );
191 5 : data_diagram_set_uuid( &((*this_).temp_diagram), data_uuid_get_string( &new_uuid ) );
192 5 : data_uuid_destroy( &new_uuid );
193 : }
194 :
195 : /* create the parsed diagram as child below the current diagram */
196 : u8_error_t modified_info;
197 5 : sync_error = ctrl_multi_step_changer_create_diagram( &((*this_).multi_step_changer),
198 : &((*this_).temp_diagram),
199 : &modified_info
200 : );
201 5 : data_stat_inc_count( (*this_).stat,
202 : DATA_STAT_TABLE_DIAGRAM,
203 : (U8_ERROR_NONE==sync_error)?DATA_STAT_SERIES_CREATED:DATA_STAT_SERIES_ERROR
204 : );
205 5 : if ( U8_ERROR_NONE != sync_error )
206 : {
207 0 : U8_LOG_ERROR( "unexpected error at ctrl_diagram_controller_create_diagram" );
208 : }
209 : else
210 : {
211 : /* insert all consecutive elements to this new diagram */
212 5 : (*this_).paste_to_diagram = data_diagram_get_row_id( &((*this_).temp_diagram) );
213 : /* this new diagram is root if it is the first diagram */
214 5 : if ( (*this_).root_diagram == DATA_ROW_ID_VOID )
215 : {
216 0 : (*this_).root_diagram = data_diagram_get_row_id( &((*this_).temp_diagram) );
217 : }
218 : }
219 5 : data_diagram_destroy( &((*this_).temp_diagram) );
220 : }
221 :
222 : /* if CREATE/LINK */
223 8 : if ((( (*this_).mode == IO_IMPORT_MODE_CREATE )||( (*this_).mode == IO_IMPORT_MODE_LINK ))
224 2 : &&( sync_error == U8_ERROR_NONE ))
225 : {
226 : /* check if the parsed diagram already exists in this database; if not, create it */
227 2 : data_diagram_init_empty( &((*this_).temp_diagram) );
228 : u8_error_t read_error;
229 2 : read_error = data_database_reader_get_diagram_by_uuid( (*this_).db_reader,
230 : data_diagram_get_uuid_const( diagram_ptr ),
231 : &((*this_).temp_diagram)
232 : );
233 2 : const bool diagram_exists = ( U8_ERROR_NONE == read_error );
234 :
235 2 : if ( diagram_exists )
236 : {
237 1 : if ( (*this_).mode == IO_IMPORT_MODE_LINK )
238 : {
239 : /* if (*this_).temp_diagram is the only valid root, set parent_row_id to DATA_ROW_ID_VOID */
240 1 : if ( data_diagram_get_row_id( &((*this_).temp_diagram) ) == (*this_).root_diagram )
241 : {
242 1 : parent_row_id = DATA_ROW_ID_VOID;
243 : }
244 :
245 : /* update the parent if wrong parent stored */
246 1 : if ( data_diagram_get_parent_row_id( &((*this_).temp_diagram) ) != parent_row_id )
247 : {
248 0 : sync_error = ctrl_multi_step_changer_update_diagram_parent_id( &((*this_).multi_step_changer),
249 0 : data_diagram_get_row_id( &((*this_).temp_diagram) ),
250 : parent_row_id
251 : );
252 :
253 : /* update root diag if this is root */
254 0 : if (( parent_row_id == DATA_ROW_ID_VOID )&&( sync_error == U8_ERROR_NONE ))
255 : {
256 0 : (*this_).root_diagram = data_diagram_get_row_id( &((*this_).temp_diagram) );
257 : }
258 : }
259 : }
260 : else
261 : {
262 : /* do the statistics */
263 0 : data_stat_inc_count( (*this_).stat, DATA_STAT_TABLE_DIAGRAM, DATA_STAT_SERIES_IGNORED );
264 : }
265 : }
266 : else
267 : {
268 1 : data_diagram_replace( &((*this_).temp_diagram), diagram_ptr );
269 1 : data_diagram_set_parent_row_id( &((*this_).temp_diagram), parent_row_id );
270 :
271 : /* create the parsed diagram as child below the current diagram */
272 : u8_error_t modified_info;
273 1 : sync_error = ctrl_multi_step_changer_create_diagram( &((*this_).multi_step_changer),
274 : &((*this_).temp_diagram),
275 : &modified_info
276 : );
277 1 : data_stat_inc_count( (*this_).stat,
278 : DATA_STAT_TABLE_DIAGRAM,
279 : (U8_ERROR_NONE==sync_error)?DATA_STAT_SERIES_CREATED:DATA_STAT_SERIES_ERROR
280 : );
281 1 : if ( u8_error_contains( modified_info, U8_ERROR_DUPLICATE_ID ) )
282 : {
283 : /* warn on changed diagram ids. This is important because links in description texts may be affected. */
284 0 : data_stat_inc_count( (*this_).stat, DATA_STAT_TABLE_DIAGRAM, DATA_STAT_SERIES_WARNING );
285 : }
286 :
287 1 : if ( U8_ERROR_NONE != sync_error )
288 : {
289 0 : U8_LOG_ERROR( "unexpected error at ctrl_diagram_controller_create_diagram" );
290 : }
291 : else
292 : {
293 : /* this new diagram is root if it is the first root diagram, further diagrams will not be root */
294 1 : if (( (*this_).root_diagram == DATA_ROW_ID_VOID )&&( parent_row_id == DATA_ROW_ID_VOID )
295 1 : &&( ! parent_uuid_specified ))
296 : {
297 1 : (*this_).root_diagram = data_diagram_get_row_id( &((*this_).temp_diagram) );
298 : }
299 : }
300 :
301 : /* write report in case of anomalies */
302 1 : if ( u8_error_contains( modified_info, U8_ERROR_DUPLICATE_ID ) )
303 : {
304 0 : io_import_elements_private_report_id_differs( this_,
305 : data_diagram_get_data_id( diagram_ptr ),
306 0 : data_diagram_get_data_id( &((*this_).temp_diagram ) )
307 : );
308 : }
309 : }
310 2 : data_diagram_destroy( &((*this_).temp_diagram) );
311 : }
312 :
313 8 : U8_TRACE_END_ERR( sync_error );
314 8 : return sync_error;
315 : }
316 :
317 7 : u8_error_t io_import_elements_sync_diagramelement( io_import_elements_t *this_,
318 : const data_diagramelement_t *diagramelement_ptr,
319 : const char *diagram_uuid,
320 : const char *node_uuid )
321 : {
322 7 : U8_TRACE_BEGIN();
323 7 : assert( NULL != diagramelement_ptr );
324 7 : assert( NULL != diagram_uuid );
325 7 : assert( NULL != node_uuid );
326 7 : u8_error_t sync_error = U8_ERROR_NONE;
327 :
328 : /* ANY MODE: determine classifier/feature id */
329 7 : data_row_id_t node_classifier_id = DATA_ROW_ID_VOID;
330 7 : data_row_id_t node_feature_id = DATA_ROW_ID_VOID;
331 7 : data_feature_type_t node_feature_type = DATA_FEATURE_TYPE_VOID;
332 7 : if ( node_uuid != NULL )
333 : {
334 7 : if ( ! utf8string_equals_str( node_uuid, "" ) )
335 : {
336 : /* search source classifier id */
337 7 : data_classifier_init_empty( &((*this_).temp_classifier ) );
338 : const u8_error_t read_error1
339 7 : = data_database_reader_get_classifier_by_uuid( (*this_).db_reader,
340 : node_uuid,
341 : &((*this_).temp_classifier)
342 : );
343 7 : if ( U8_ERROR_NONE == read_error1 )
344 : {
345 2 : node_classifier_id = data_classifier_get_row_id( &((*this_).temp_classifier) );
346 2 : U8_TRACE_INFO_STR( "id found for classifier:", node_uuid );
347 : }
348 : else
349 : {
350 : /* search source feature id */
351 5 : data_feature_init_empty( &((*this_).temp_feature) );
352 : const u8_error_t read_error2
353 5 : = data_database_reader_get_feature_by_uuid( (*this_).db_reader,
354 : node_uuid,
355 : &((*this_).temp_feature)
356 : );
357 5 : if ( U8_ERROR_NONE == read_error2 )
358 : {
359 0 : node_classifier_id = data_feature_get_classifier_row_id( &((*this_).temp_feature) );
360 0 : node_feature_id = data_feature_get_row_id( &((*this_).temp_feature) );
361 0 : node_feature_type = data_feature_get_main_type( &((*this_).temp_feature) );
362 0 : U8_TRACE_INFO_STR( "id found for feature:", node_uuid );
363 : }
364 : else
365 : {
366 5 : U8_TRACE_INFO_STR( "diagramelement node not found", node_uuid );
367 : }
368 5 : data_feature_destroy( &((*this_).temp_feature) );
369 : }
370 7 : data_classifier_destroy( &((*this_).temp_classifier ) );
371 : }
372 : }
373 :
374 : /* ANY MODE: determine diagram id */
375 7 : data_row_id_t diagram_row_id = DATA_ROW_ID_VOID;
376 7 : if (( diagram_uuid != NULL )&&( sync_error == U8_ERROR_NONE ))
377 : {
378 7 : if ( ! utf8string_equals_str( diagram_uuid, "" ) )
379 : {
380 7 : data_diagram_init_empty( &((*this_).temp_diagram ) );
381 : const u8_error_t read_error3
382 7 : = data_database_reader_get_diagram_by_uuid( (*this_).db_reader,
383 : diagram_uuid,
384 : &((*this_).temp_diagram)
385 : );
386 7 : if ( read_error3 == U8_ERROR_NOT_FOUND )
387 : {
388 5 : U8_TRACE_INFO_STR( "no diagram found, uuid:", diagram_uuid );
389 : }
390 2 : else if ( read_error3 != U8_ERROR_NONE )
391 : {
392 0 : U8_TRACE_INFO_STR( "diagram not found:", diagram_uuid );
393 : }
394 : else
395 : {
396 2 : diagram_row_id = data_diagram_get_row_id( &((*this_).temp_diagram ) );
397 : }
398 7 : data_diagram_destroy( &((*this_).temp_diagram ) );
399 : }
400 : }
401 :
402 : /* check preconditions */
403 7 : if ( (*this_).mode == IO_IMPORT_MODE_LINK )
404 : {
405 1 : if ( node_classifier_id == DATA_ROW_ID_VOID )
406 : {
407 0 : sync_error |= U8_ERROR_VALUE_OUT_OF_RANGE;
408 0 : U8_LOG_ERROR( "diagramelement references a non-existing classifier." );
409 : }
410 1 : if (( node_feature_id != DATA_ROW_ID_VOID )&&( node_feature_type != DATA_FEATURE_TYPE_LIFELINE ))
411 : {
412 0 : sync_error |= U8_ERROR_VALUE_OUT_OF_RANGE;
413 0 : U8_LOG_ERROR( "diagramelement references a feature which is not of type DATA_FEATURE_TYPE_LIFELINE." );
414 : }
415 1 : if ( diagram_row_id == DATA_ROW_ID_VOID )
416 : {
417 0 : sync_error |= U8_ERROR_VALUE_OUT_OF_RANGE;
418 0 : U8_LOG_ERROR( "diagramelement references a non-existing classifier." );
419 : }
420 : }
421 :
422 7 : if (( (*this_).mode == IO_IMPORT_MODE_LINK )&&( sync_error == U8_ERROR_NONE ))
423 : {
424 : /* check if the parsed diagramelement already exists in this database; if not, create it */
425 1 : data_diagramelement_init_empty( &((*this_).temp_diagramelement ) );
426 : const u8_error_t read_error4
427 1 : = data_database_reader_get_diagramelement_by_uuid( (*this_).db_reader,
428 : data_diagramelement_get_uuid_const( diagramelement_ptr ),
429 : &((*this_).temp_diagramelement)
430 : );
431 1 : const bool diagramelement_exists = ( U8_ERROR_NONE == read_error4 );
432 :
433 1 : if ( diagramelement_exists )
434 : {
435 : /* do the statistics */
436 0 : data_stat_inc_count( (*this_).stat, DATA_STAT_TABLE_DIAGRAMELEMENT, DATA_STAT_SERIES_IGNORED );
437 0 : U8_TRACE_INFO_INT( "diagramelement did already exist:", data_diagramelement_get_row_id( &((*this_).temp_diagramelement) ) );
438 : }
439 : else
440 : {
441 : /* link the classifier to the current diagram */
442 1 : sync_error = data_diagramelement_reinit( &((*this_).temp_diagramelement),
443 : data_diagramelement_get_row_id( diagramelement_ptr ),
444 : diagram_row_id,
445 : node_classifier_id,
446 : data_diagramelement_get_display_flags( diagramelement_ptr ),
447 : node_feature_id, /* focused_feature_id */
448 : data_diagramelement_get_uuid_const( diagramelement_ptr )
449 : );
450 : u8_error_t modified_info;
451 1 : sync_error |= ctrl_multi_step_changer_create_diagramelement( &((*this_).multi_step_changer),
452 : &((*this_).temp_diagramelement),
453 : &modified_info
454 : );
455 1 : data_stat_inc_count( (*this_).stat,
456 : DATA_STAT_TABLE_DIAGRAMELEMENT,
457 : (U8_ERROR_NONE==sync_error)?DATA_STAT_SERIES_CREATED:DATA_STAT_SERIES_ERROR
458 : );
459 1 : if ( U8_ERROR_NONE != sync_error )
460 : {
461 0 : U8_LOG_ERROR( "unexpected error at ctrl_diagram_controller_create_diagramelement" );
462 : }
463 :
464 : /* write report in case of anomalies */
465 1 : if ( u8_error_contains( modified_info, U8_ERROR_DUPLICATE_ID ) )
466 : {
467 0 : io_import_elements_private_report_id_differs( this_,
468 : data_diagramelement_get_data_id( diagramelement_ptr ),
469 0 : data_diagramelement_get_data_id( &((*this_).temp_diagramelement ) )
470 : );
471 : }
472 : }
473 1 : data_diagramelement_destroy( &((*this_).temp_diagramelement ) );
474 : }
475 :
476 7 : if ( (*this_).mode == IO_IMPORT_MODE_PASTE )
477 : {
478 : /* in paste mode, ignore diagramelements */
479 4 : data_stat_inc_count( (*this_).stat, DATA_STAT_TABLE_DIAGRAMELEMENT, DATA_STAT_SERIES_IGNORED );
480 : }
481 :
482 7 : U8_TRACE_END_ERR( sync_error );
483 7 : return sync_error;
484 : }
485 :
486 15 : u8_error_t io_import_elements_private_create_diagramelement( io_import_elements_t *this_, data_row_id_t classifier_id )
487 : {
488 15 : U8_TRACE_BEGIN();
489 15 : assert( DATA_ROW_ID_VOID != classifier_id );
490 15 : u8_error_t sync_error = U8_ERROR_NONE;
491 :
492 15 : if ( (*this_).mode == IO_IMPORT_MODE_PASTE )
493 : {
494 : /* link the classifier to the current diagram */
495 15 : data_diagramelement_init_new( &((*this_).temp_diagramelement ),
496 : (*this_).paste_to_diagram,
497 : classifier_id,
498 : DATA_DIAGRAMELEMENT_FLAG_NONE,
499 : DATA_ROW_ID_VOID
500 : );
501 : u8_error_t modified_info;
502 15 : sync_error = ctrl_multi_step_changer_create_diagramelement( &((*this_).multi_step_changer),
503 : &((*this_).temp_diagramelement ),
504 : &modified_info
505 : );
506 15 : data_stat_inc_count( (*this_).stat,
507 : DATA_STAT_TABLE_DIAGRAMELEMENT,
508 : (U8_ERROR_NONE==sync_error)?DATA_STAT_SERIES_CREATED:DATA_STAT_SERIES_ERROR
509 : );
510 15 : if ( U8_ERROR_NONE != sync_error )
511 : {
512 0 : U8_LOG_ERROR( "unexpected error at ctrl_diagram_controller_create_diagramelement" );
513 : }
514 :
515 15 : data_diagramelement_destroy( &((*this_).temp_diagramelement ) );
516 : }
517 : else
518 : {
519 0 : U8_TRACE_INFO_INT( "no diagramelement created for new classifier:", classifier_id );
520 : }
521 :
522 15 : U8_TRACE_END_ERR( sync_error );
523 15 : return sync_error;
524 : }
525 :
526 19 : u8_error_t io_import_elements_sync_classifier( io_import_elements_t *this_,
527 : const data_classifier_t *classifier_ptr )
528 : {
529 19 : U8_TRACE_BEGIN();
530 19 : assert( NULL != classifier_ptr );
531 19 : u8_error_t sync_error = U8_ERROR_NONE;
532 :
533 19 : if ( (*this_).mode == IO_IMPORT_MODE_PASTE )
534 : {
535 16 : if ( (*this_).paste_to_diagram == DATA_ROW_ID_VOID )
536 : {
537 1 : sync_error = U8_ERROR_FOCUS_EMPTY;
538 1 : U8_TRACE_INFO( "in paste-clipboard mode, parent diagram must be valid" );
539 : }
540 : }
541 :
542 19 : if ((( (*this_).mode == IO_IMPORT_MODE_CREATE )||( (*this_).mode == IO_IMPORT_MODE_PASTE ))
543 17 : &&( sync_error == U8_ERROR_NONE ))
544 : {
545 : /* check if the parsed classifier already exists in this database; if not, create it */
546 16 : data_classifier_init_empty( &((*this_).temp_classifier ) );
547 : u8_error_t read_error;
548 16 : read_error = data_database_reader_get_classifier_by_uuid( (*this_).db_reader,
549 : data_classifier_get_uuid_const( classifier_ptr ),
550 : &((*this_).temp_classifier)
551 : );
552 16 : const bool classifier_exists = ( U8_ERROR_NONE == read_error );
553 :
554 16 : if ( classifier_exists )
555 : {
556 : /* do the statistics */
557 3 : data_stat_inc_count( (*this_).stat, DATA_STAT_TABLE_CLASSIFIER, DATA_STAT_SERIES_IGNORED );
558 3 : U8_TRACE_INFO_INT( "classifier did already exist:", data_classifier_get_row_id( &((*this_).temp_classifier) ) );
559 : }
560 : else
561 : {
562 13 : data_classifier_replace( &((*this_).temp_classifier ), classifier_ptr );
563 :
564 : u8_error_t modified_info;
565 13 : sync_error = ctrl_multi_step_changer_create_classifier( &((*this_).multi_step_changer),
566 : &((*this_).temp_classifier ),
567 : &modified_info
568 : );
569 13 : data_stat_inc_count( (*this_).stat,
570 : DATA_STAT_TABLE_CLASSIFIER,
571 : (U8_ERROR_NONE==sync_error)?DATA_STAT_SERIES_CREATED:DATA_STAT_SERIES_ERROR
572 : );
573 13 : if ( u8_error_contains( modified_info, U8_ERROR_DUPLICATE_NAME ) )
574 : {
575 : /* warn on changed classifier names. */
576 3 : data_stat_inc_count( (*this_).stat, DATA_STAT_TABLE_CLASSIFIER, DATA_STAT_SERIES_WARNING );
577 : }
578 :
579 13 : if ( U8_ERROR_NONE != sync_error )
580 : {
581 0 : U8_LOG_ERROR( "unexpected error at ctrl_classifier_controller_create_classifier/feature" );
582 : }
583 :
584 : /* write report in case of anomalies */
585 13 : if ( u8_error_contains( modified_info, U8_ERROR_DUPLICATE_ID ) )
586 : {
587 0 : io_import_elements_private_report_id_differs( this_,
588 : data_classifier_get_data_id( classifier_ptr ),
589 0 : data_classifier_get_data_id( &((*this_).temp_classifier ) )
590 : );
591 : }
592 13 : if ( u8_error_contains( modified_info, U8_ERROR_DUPLICATE_NAME ) )
593 : {
594 6 : io_import_elements_private_report_name_differs( this_,
595 : data_classifier_get_name_const( classifier_ptr ),
596 3 : data_classifier_get_name_const( &((*this_).temp_classifier ) )
597 : );
598 : }
599 : }
600 :
601 16 : if (( (*this_).mode == IO_IMPORT_MODE_PASTE )&&( sync_error == U8_ERROR_NONE ))
602 : {
603 : /* in paste mode, create a diagramelement in the current diagram */
604 15 : const data_row_id_t classifier_row_id = data_classifier_get_row_id( &((*this_).temp_classifier ) );
605 15 : sync_error = io_import_elements_private_create_diagramelement( this_, classifier_row_id );
606 : }
607 :
608 16 : data_classifier_destroy( &((*this_).temp_classifier ) );
609 : }
610 :
611 19 : U8_TRACE_END_ERR( sync_error );
612 19 : return sync_error;
613 : }
614 :
615 22 : u8_error_t io_import_elements_sync_feature( io_import_elements_t *this_,
616 : const data_feature_t *feature_ptr,
617 : const char *classifier_uuid )
618 : {
619 22 : U8_TRACE_BEGIN();
620 22 : assert( NULL != feature_ptr );
621 22 : assert( NULL != classifier_uuid );
622 22 : u8_error_t sync_error = U8_ERROR_NONE;
623 :
624 : /* ANY MODE: determine classifier id */
625 22 : data_row_id_t classifier_row_id = DATA_ROW_ID_VOID;
626 22 : if ( classifier_uuid != NULL )
627 : {
628 22 : if ( ! utf8string_equals_str( classifier_uuid, "" ) )
629 : {
630 22 : data_classifier_init_empty( &((*this_).temp_classifier ) );
631 : const u8_error_t read_error1
632 22 : = data_database_reader_get_classifier_by_uuid( (*this_).db_reader,
633 : classifier_uuid,
634 : &((*this_).temp_classifier)
635 : );
636 22 : if ( read_error1 == U8_ERROR_NOT_FOUND )
637 : {
638 2 : U8_TRACE_INFO_STR( "no classifier found, uuid:", classifier_uuid );
639 : }
640 20 : else if ( read_error1 != U8_ERROR_NONE )
641 : {
642 0 : U8_TRACE_INFO_STR( "parent classifier not found:", classifier_uuid );
643 : }
644 : else
645 : {
646 20 : classifier_row_id = data_classifier_get_row_id( &((*this_).temp_classifier ) );
647 : }
648 22 : data_classifier_destroy( &((*this_).temp_classifier ) );
649 : }
650 : }
651 :
652 : /* check preconditions */
653 22 : if (( (*this_).mode == IO_IMPORT_MODE_CREATE )||( (*this_).mode == IO_IMPORT_MODE_PASTE ))
654 : {
655 18 : if ( classifier_row_id == DATA_ROW_ID_VOID )
656 : {
657 0 : sync_error |= U8_ERROR_VALUE_OUT_OF_RANGE;
658 0 : U8_LOG_ERROR( "feature references a non-existing classifier." );
659 : }
660 : }
661 :
662 22 : if ((( (*this_).mode == IO_IMPORT_MODE_CREATE )||( (*this_).mode == IO_IMPORT_MODE_PASTE ))
663 18 : &&( sync_error == U8_ERROR_NONE ))
664 : {
665 : /* check if the parsed feature already exists in this database; if not, create it */
666 18 : data_feature_init_empty( &((*this_).temp_feature ) );
667 : u8_error_t read_error;
668 18 : read_error = data_database_reader_get_feature_by_uuid( (*this_).db_reader,
669 : data_feature_get_uuid_const( feature_ptr ),
670 : &((*this_).temp_feature)
671 : );
672 18 : data_feature_destroy( &((*this_).temp_feature ) );
673 18 : const bool feature_exists = ( U8_ERROR_NONE == read_error );
674 :
675 18 : if ( feature_exists )
676 : {
677 : /* update statistics */
678 1 : const data_feature_type_t feat_type = data_feature_get_main_type( &((*this_).temp_feature) );
679 1 : const data_stat_table_t feat_or_lifeline
680 1 : = ( feat_type == DATA_FEATURE_TYPE_LIFELINE ) ? DATA_STAT_TABLE_LIFELINE : DATA_STAT_TABLE_FEATURE;
681 1 : data_stat_inc_count( (*this_).stat, feat_or_lifeline, DATA_STAT_SERIES_IGNORED );
682 : }
683 : else
684 : {
685 : /* filter lifelines */
686 : const bool is_lifeline
687 17 : = data_rules_feature_is_scenario_cond( &((*this_).data_rules), data_feature_get_main_type( feature_ptr ) );
688 17 : if (( (*this_).mode == IO_IMPORT_MODE_CREATE )||( ! is_lifeline ))
689 9 : {
690 : /* create feature */
691 9 : data_feature_copy( &((*this_).temp_feature ), feature_ptr );
692 9 : data_feature_set_classifier_row_id( &((*this_).temp_feature ), classifier_row_id );
693 :
694 : u8_error_t modified_info;
695 9 : sync_error = ctrl_multi_step_changer_create_feature( &((*this_).multi_step_changer),
696 : &((*this_).temp_feature ),
697 : &modified_info
698 : );
699 :
700 9 : data_stat_inc_count( (*this_).stat,
701 : is_lifeline ? DATA_STAT_TABLE_LIFELINE : DATA_STAT_TABLE_FEATURE,
702 : (U8_ERROR_NONE==sync_error)?DATA_STAT_SERIES_CREATED:DATA_STAT_SERIES_ERROR
703 : );
704 9 : if ( U8_ERROR_NONE != sync_error )
705 : {
706 0 : U8_LOG_ERROR( "unexpected error at ctrl_classifier_controller_create_feature" );
707 : }
708 :
709 : /* write report in case of anomalies */
710 9 : if ( u8_error_contains( modified_info, U8_ERROR_DUPLICATE_ID ) )
711 : {
712 2 : io_import_elements_private_report_id_differs( this_,
713 : data_feature_get_data_id( feature_ptr ),
714 1 : data_feature_get_data_id( &((*this_).temp_feature ) )
715 : );
716 : }
717 :
718 9 : data_feature_destroy( &((*this_).temp_feature ) );
719 : }
720 : else /* lifeline in paste mode */
721 : {
722 8 : assert( is_lifeline );
723 8 : data_stat_inc_count( (*this_).stat,
724 : DATA_STAT_TABLE_LIFELINE, /* is_lifeline is true */
725 : DATA_STAT_SERIES_IGNORED
726 : );
727 8 : U8_TRACE_INFO( "lifeline dropped at json import." );
728 : }
729 : }
730 : }
731 :
732 22 : U8_TRACE_END_ERR( sync_error );
733 22 : return sync_error;
734 : }
735 :
736 13 : u8_error_t io_import_elements_sync_relationship( io_import_elements_t *this_,
737 : const data_relationship_t *relation_ptr,
738 : const char *from_node_uuid,
739 : const char *to_node_uuid )
740 : {
741 13 : U8_TRACE_BEGIN();
742 13 : assert( NULL != relation_ptr );
743 13 : assert( NULL != from_node_uuid );
744 13 : assert( NULL != to_node_uuid );
745 13 : u8_error_t sync_error = U8_ERROR_NONE;
746 :
747 : /* ANY MODE: determine from classifier/feature */
748 13 : data_row_id_t from_classifier_id = DATA_ROW_ID_VOID;
749 13 : data_row_id_t from_feature_id = DATA_ROW_ID_VOID;
750 13 : data_feature_type_t from_feature_type = DATA_FEATURE_TYPE_VOID;
751 13 : if ( from_node_uuid != NULL )
752 : {
753 13 : if ( ! utf8string_equals_str( from_node_uuid, "" ) )
754 : {
755 : /* search source classifier id */
756 13 : data_classifier_init_empty( &((*this_).temp_classifier ) );
757 : const u8_error_t read_error1
758 13 : = data_database_reader_get_classifier_by_uuid( (*this_).db_reader,
759 : from_node_uuid,
760 : &((*this_).temp_classifier)
761 : );
762 13 : if ( U8_ERROR_NONE == read_error1 )
763 : {
764 8 : from_classifier_id = data_classifier_get_row_id( &((*this_).temp_classifier) );
765 8 : U8_TRACE_INFO_STR( "id found for src classifier:", from_node_uuid );
766 : }
767 : else
768 : {
769 : /* search source feature id */
770 5 : data_feature_init_empty( &((*this_).temp_feature) );
771 : const u8_error_t read_error2
772 5 : = data_database_reader_get_feature_by_uuid( (*this_).db_reader,
773 : from_node_uuid,
774 : &((*this_).temp_feature)
775 : );
776 5 : if ( U8_ERROR_NONE == read_error2 )
777 : {
778 1 : from_classifier_id = data_feature_get_classifier_row_id( &((*this_).temp_feature) );
779 1 : from_feature_id = data_feature_get_row_id( &((*this_).temp_feature) );
780 1 : from_feature_type = data_feature_get_main_type( &((*this_).temp_feature) );
781 1 : U8_TRACE_INFO_STR( "id found for src feature:", from_node_uuid );
782 : }
783 : else
784 : {
785 4 : U8_TRACE_INFO_STR( "relationship source not found", from_node_uuid );
786 : }
787 5 : data_feature_destroy( &((*this_).temp_feature) );
788 : }
789 13 : data_classifier_destroy( &((*this_).temp_classifier ) );
790 : }
791 : }
792 :
793 : /* ANY MODE: determine to classifier/feature */
794 13 : data_row_id_t to_classifier_id = DATA_ROW_ID_VOID;
795 13 : data_row_id_t to_feature_id = DATA_ROW_ID_VOID;
796 13 : data_feature_type_t to_feature_type = DATA_FEATURE_TYPE_VOID;
797 13 : if ( to_node_uuid != NULL )
798 : {
799 13 : if ( ! utf8string_equals_str( to_node_uuid, "" ) )
800 : {
801 : /* search destination classifier id */
802 13 : data_classifier_init_empty( &((*this_).temp_classifier ) );
803 : const u8_error_t read_error3
804 13 : = data_database_reader_get_classifier_by_uuid( (*this_).db_reader,
805 : to_node_uuid,
806 : &((*this_).temp_classifier)
807 : );
808 13 : if ( U8_ERROR_NONE == read_error3 )
809 : {
810 7 : to_classifier_id = data_classifier_get_row_id( &((*this_).temp_classifier) );
811 7 : U8_TRACE_INFO_STR( "id found for dst classifier:", to_node_uuid );
812 : }
813 : else
814 : {
815 : /* search dst feature id */
816 6 : data_feature_init_empty( &((*this_).temp_feature) );
817 : const u8_error_t read_error4
818 6 : = data_database_reader_get_feature_by_uuid( (*this_).db_reader,
819 : to_node_uuid,
820 : &((*this_).temp_feature)
821 : );
822 6 : if ( U8_ERROR_NONE == read_error4 )
823 : {
824 5 : to_classifier_id = data_feature_get_classifier_row_id( &((*this_).temp_feature) );
825 5 : to_feature_id = data_feature_get_row_id( &((*this_).temp_feature) );
826 5 : to_feature_type = data_feature_get_main_type( &((*this_).temp_feature) );
827 5 : U8_TRACE_INFO_STR( "id found for src feature:", to_node_uuid );
828 : }
829 : else
830 : {
831 1 : U8_TRACE_INFO_STR( "relationship destination not found", to_node_uuid );
832 : }
833 6 : data_feature_destroy( &((*this_).temp_feature) );
834 : }
835 13 : data_classifier_destroy( &((*this_).temp_classifier ) );
836 : }
837 : }
838 :
839 : /* check preconditions */
840 13 : if (( (*this_).mode == IO_IMPORT_MODE_PASTE )||( (*this_).mode == IO_IMPORT_MODE_LINK ))
841 : {
842 11 : if ( from_classifier_id == DATA_ROW_ID_VOID )
843 : {
844 3 : sync_error |= U8_ERROR_VALUE_OUT_OF_RANGE;
845 3 : U8_LOG_ERROR( "A relationship could not be created because the source classifier could not be found." );
846 : }
847 11 : if ( to_classifier_id == DATA_ROW_ID_VOID )
848 : {
849 0 : sync_error |= U8_ERROR_VALUE_OUT_OF_RANGE;
850 0 : U8_LOG_ERROR( "A relationship could not be created because the destination classifier could not be found." );
851 : }
852 11 : if ( sync_error != U8_ERROR_NONE )
853 : {
854 3 : const bool is_scenario = data_rules_relationship_is_scenario_cond( &((*this_).data_rules),
855 : from_feature_type,
856 : to_feature_type
857 : );
858 3 : U8_TRACE_INFO( is_scenario ? "relationship in interaction scenario dropped" : "general relationship dropped" );
859 : }
860 : }
861 :
862 13 : if ((( (*this_).mode == IO_IMPORT_MODE_PASTE )||( (*this_).mode == IO_IMPORT_MODE_LINK ))
863 11 : &&( sync_error == U8_ERROR_NONE ))
864 : {
865 : /* check if the parsed relationship already exists in this database; if not, create it */
866 8 : data_relationship_init_empty( &((*this_).temp_relationship ) );
867 : const u8_error_t read_error5
868 8 : = data_database_reader_get_relationship_by_uuid( (*this_).db_reader,
869 : data_relationship_get_uuid_const( relation_ptr ),
870 : &((*this_).temp_relationship)
871 : );
872 8 : data_relationship_destroy( &((*this_).temp_relationship ) );
873 8 : const bool relationship_exists = ( U8_ERROR_NONE == read_error5 );
874 :
875 8 : if ( relationship_exists )
876 : {
877 : /* update statistics */
878 1 : data_stat_inc_count( (*this_).stat, DATA_STAT_TABLE_RELATIONSHIP, DATA_STAT_SERIES_IGNORED );
879 : }
880 : else
881 : {
882 : /* create relationship */
883 7 : data_relationship_copy( &((*this_).temp_relationship ), relation_ptr );
884 7 : data_relationship_set_row_id( &((*this_).temp_relationship ), data_relationship_get_row_id( relation_ptr ) );
885 7 : data_relationship_set_from_classifier_row_id( &((*this_).temp_relationship ), from_classifier_id );
886 7 : data_relationship_set_from_feature_row_id( &((*this_).temp_relationship ), from_feature_id );
887 7 : data_relationship_set_to_classifier_row_id( &((*this_).temp_relationship ), to_classifier_id );
888 7 : data_relationship_set_to_feature_row_id( &((*this_).temp_relationship ), to_feature_id );
889 :
890 : /* create relationship */
891 : u8_error_t modified_info;
892 7 : sync_error = ctrl_multi_step_changer_create_relationship( &((*this_).multi_step_changer),
893 : &((*this_).temp_relationship ),
894 : &modified_info
895 : );
896 7 : if ( U8_ERROR_NONE != sync_error )
897 : {
898 0 : U8_LOG_ERROR( "unexpected error at ctrl_classifier_controller_create_relationship" );
899 : }
900 : else
901 : {
902 : }
903 :
904 : /* update statistics */
905 7 : data_stat_inc_count( (*this_).stat,
906 : DATA_STAT_TABLE_RELATIONSHIP,
907 : (U8_ERROR_NONE==sync_error)?DATA_STAT_SERIES_CREATED:DATA_STAT_SERIES_ERROR
908 : );
909 :
910 : /* write report in case of anomalies */
911 7 : if ( u8_error_contains( modified_info, U8_ERROR_DUPLICATE_ID ) )
912 : {
913 4 : io_import_elements_private_report_id_differs( this_,
914 : data_relationship_get_data_id( relation_ptr ),
915 2 : data_relationship_get_data_id( &((*this_).temp_relationship ) )
916 : );
917 : }
918 :
919 7 : data_relationship_destroy( &((*this_).temp_relationship ) );
920 : }
921 : }
922 :
923 13 : U8_TRACE_END_ERR( sync_error );
924 13 : return sync_error;
925 : }
926 :
927 3 : void io_import_elements_private_report_id_differs( io_import_elements_t *this_, data_id_t req_id, data_id_t act_id )
928 : {
929 3 : U8_TRACE_BEGIN();
930 3 : u8_error_t report_err = U8_ERROR_NONE;
931 :
932 3 : report_err |= utf8stream_writer_write_str( (*this_).english_report, "Id changed: " );
933 3 : report_err |= data_id_to_utf8_writer( &req_id, (*this_).english_report );
934 3 : report_err |= utf8stream_writer_write_str( (*this_).english_report, " -> " );
935 3 : report_err |= data_id_to_utf8_writer( &act_id, (*this_).english_report );
936 3 : report_err |= utf8stream_writer_write_str( (*this_).english_report, ", " );
937 3 : if ( report_err != U8_ERROR_NONE )
938 : {
939 0 : U8_LOG_ERROR_HEX( "Could not write report on import, ERR:", report_err );
940 : }
941 :
942 3 : U8_TRACE_END();
943 3 : }
944 :
945 3 : void io_import_elements_private_report_name_differs( io_import_elements_t *this_, const char *req_name, const char *act_name )
946 : {
947 3 : U8_TRACE_BEGIN();
948 3 : assert( NULL != req_name );
949 3 : assert( NULL != act_name );
950 3 : u8_error_t report_err = U8_ERROR_NONE;
951 :
952 3 : report_err |= utf8stream_writer_write_str( (*this_).english_report, "Name changed: \"" );
953 3 : report_err |= utf8stream_writer_write_str( (*this_).english_report, req_name );
954 3 : report_err |= utf8stream_writer_write_str( (*this_).english_report, "\" -> \"" );
955 3 : report_err |= utf8stream_writer_write_str( (*this_).english_report, act_name );
956 3 : report_err |= utf8stream_writer_write_str( (*this_).english_report, "\", " );
957 3 : if ( report_err != U8_ERROR_NONE )
958 : {
959 0 : U8_LOG_ERROR_HEX( "Could not write report on import, ERR:", report_err );
960 : }
961 :
962 3 : U8_TRACE_END();
963 3 : }
964 :
965 :
966 : /*
967 : Copyright 2021-2024 Andreas Warnke
968 :
969 : Licensed under the Apache License, Version 2.0 (the "License");
970 : you may not use this file except in compliance with the License.
971 : You may obtain a copy of the License at
972 :
973 : http://www.apache.org/licenses/LICENSE-2.0
974 :
975 : Unless required by applicable law or agreed to in writing, software
976 : distributed under the License is distributed on an "AS IS" BASIS,
977 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
978 : See the License for the specific language governing permissions and
979 : limitations under the License.
980 : */
|