@@ -2,6 +2,7 @@ use std::{collections::HashMap, hash::Hash};
22
33use crate :: * ;
44
5+ #[ derive( Debug ) ]
56// actions to be taken when merging two elements
67enum MergeAction {
78 MergeEqual ,
@@ -232,7 +233,17 @@ impl AutosarModel {
232233 let root = self . root_element ( ) ;
233234 let files: HashSet < WeakArxmlFile > = self . files ( ) . map ( |f| f. downgrade ( ) ) . collect ( ) ;
234235
235- Self :: merge_element ( & root, & files, new_root, & new_file) ?;
236+ Self :: merge_element ( & root, & files, new_root, & new_file) . map_err ( |e| {
237+ // transform ElementInsertionConflict into InvalidFileMerge
238+ if let AutosarDataError :: ElementInsertionConflict { parent_path, .. } = & e {
239+ AutosarDataError :: InvalidFileMerge {
240+ path : parent_path. clone ( ) ,
241+ }
242+ } else {
243+ e
244+ }
245+ } ) ?;
246+
236247 self . root_element ( ) . 0 . write ( ) . file_membership . insert ( new_file) ;
237248
238249 Ok ( ( ) )
@@ -340,11 +351,13 @@ impl AutosarModel {
340351 files. clone_into ( & mut elem_locked. file_membership ) ;
341352 }
342353 }
354+
343355 // elements in elements_b_only are not present in the model yet, so they need to be added
356+ // this step can fail, in which case the merge of this element fails
344357 Self :: import_new_items ( parent_a, elements_b_only, new_file, min_ver_b) ?;
345358
346359 // recurse for sub elements that are present on both sides: these need to be checked and merged
347- Self :: merge_sub_elements ( elements_merge, files, new_file) ?;
360+ Self :: merge_sub_elements ( parent_a , elements_merge, files, new_file, version ) ?;
348361
349362 Ok ( ( ) )
350363 }
@@ -400,9 +413,15 @@ impl AutosarModel {
400413
401414 if defref_a == defref_b {
402415 // either: defrefs exist and are identical, OR they are both None.
403- // if they are None, then there is nothing else that can be compared, so we just assume the elements are identical.
404- // Merge them and advance both iterators.
405- MergeAction :: MergeEqual
416+ if elem_a. character_data ( ) != elem_b. character_data ( ) {
417+ // they have different character data, so they are not identical
418+ // take only a, defer b
419+ MergeAction :: AOnly
420+ } else {
421+ // if they are None, then there is nothing else that can be compared, so we just assume the elements are identical.
422+ // Merge them and advance both iterators.
423+ MergeAction :: MergeEqual
424+ }
406425 } else {
407426 // check if a sibling of elem_b has the same definiton-ref as elem_a
408427 // this handles the case where the the elements on both sides are ordered differently
@@ -430,41 +449,53 @@ impl AutosarModel {
430449 parent_a : & Element ,
431450 elements_b_only : Vec < ( Element , usize ) > ,
432451 new_file : & WeakArxmlFile ,
433- min_ver_b : AutosarVersion ,
452+ version : AutosarVersion ,
434453 ) -> Result < ( ) , AutosarDataError > {
435454 // elements in elements_b_only are not present in the model yet, so they need to be added
436- let mut parent_a_locked = parent_a. 0 . write ( ) ;
437455 for ( idx, ( new_element, insert_pos) ) in elements_b_only. into_iter ( ) . enumerate ( ) {
438- new_element. set_parent ( ElementOrModel :: Element ( parent_a. downgrade ( ) ) ) ;
439- // restrict new_element, it is only present in new_file
440- new_element. 0 . write ( ) . file_membership . insert ( new_file. clone ( ) ) ;
441-
442- // add the new_element (from side b) to the content of parent_a
443- // to do this, first check valid element insertion positions
444- let ( first_pos, last_pos) = parent_a_locked
445- . calc_element_insert_range ( new_element. element_name ( ) , min_ver_b)
446- . map_err ( |_| AutosarDataError :: InvalidFileMerge {
447- path : new_element. element_name ( ) . to_string ( ) ,
448- } ) ?;
449-
450456 // idx number of elements have already been inserted, so the destination position must be adjusted
451457 let dest = insert_pos + idx;
452458
453- // clamp dest, so that first_pos <= dest <= last_pos
454- let dest = dest. max ( first_pos) . min ( last_pos) ;
455-
456- // insert the element from b at the calculated position
457- parent_a_locked
458- . content
459- . insert ( dest, ElementContent :: Element ( new_element) ) ;
459+ Self :: import_single_item ( parent_a, new_element, dest, new_file, version) ?;
460460 }
461461 Ok ( ( ) )
462462 }
463463
464+ fn import_single_item (
465+ parent_a : & Element ,
466+ new_element : Element ,
467+ dest : usize ,
468+ new_file : & WeakArxmlFile ,
469+ version : AutosarVersion ,
470+ ) -> Result < ( ) , AutosarDataError > {
471+ let mut parent_a_locked = parent_a. 0 . write ( ) ;
472+ let weak_parent_a = parent_a. downgrade ( ) ;
473+
474+ new_element. set_parent ( ElementOrModel :: Element ( weak_parent_a) ) ;
475+ // restrict new_element, it is only present in new_file
476+ new_element. 0 . write ( ) . file_membership . insert ( new_file. clone ( ) ) ;
477+
478+ // add the new_element (from side b) to the content of parent_a
479+ // to do this, first check valid element insertion positions
480+ let ( first_pos, last_pos) = parent_a_locked. calc_element_insert_range ( new_element. element_name ( ) , version) ?;
481+
482+ // clamp dest, so that first_pos <= dest <= last_pos
483+ let dest = dest. max ( first_pos) . min ( last_pos) ;
484+
485+ // insert the element from b at the calculated position
486+ parent_a_locked
487+ . content
488+ . insert ( dest, ElementContent :: Element ( new_element) ) ;
489+
490+ Ok ( ( ) )
491+ }
492+
464493 fn merge_sub_elements (
494+ parent_a : & Element ,
465495 elements_merge : Vec < ( Element , Element ) > ,
466496 files : & HashSet < WeakArxmlFile > ,
467497 new_file : & WeakArxmlFile ,
498+ version : AutosarVersion ,
468499 ) -> Result < ( ) , AutosarDataError > {
469500 for ( elem_a, elem_b) in elements_merge {
470501 // get the list of files that the element from a is present in
@@ -475,12 +506,34 @@ impl AutosarModel {
475506 } ;
476507
477508 // merge the two elements
478- AutosarModel :: merge_element ( & elem_a, & files, & elem_b, new_file) ?;
509+ let result = AutosarModel :: merge_element ( & elem_a, & files, & elem_b, new_file) ;
510+ match result {
511+ Ok ( ( ) ) => {
512+ // update the file membership of the merged element, if there was any
513+ let mut elem_a_locked = elem_a. 0 . write ( ) ;
514+ if !elem_a_locked. file_membership . is_empty ( ) {
515+ elem_a_locked. file_membership . insert ( new_file. clone ( ) ) ;
516+ }
517+ }
518+ Err ( e) => {
519+ if let AutosarDataError :: ElementInsertionConflict { parent_path, .. } = & e {
520+ // failed to merge sub element due to insertion conflict
521+ if elem_a. is_identifiable ( ) {
522+ // if the merge of an identifiable element fails, then the whole merge fails
523+ return Err ( AutosarDataError :: InvalidFileMerge {
524+ path : parent_path. clone ( ) ,
525+ } ) ;
526+ } else if parent_a. element_type ( ) . splittable_in ( version) {
527+ elem_a. set_file_membership ( files) ;
528+ // try to import elem_b as a new item instead
529+ let dest = elem_a. position ( ) . unwrap_or_default ( ) + 1 ;
530+ return Self :: import_single_item ( parent_a, elem_b, dest, new_file, version) . map_err ( |_| e) ;
531+ }
532+ }
479533
480- // update the file membership of the merged element, if there was any
481- let mut elem_a_locked = elem_a. 0 . write ( ) ;
482- if !elem_a_locked. file_membership . is_empty ( ) {
483- elem_a_locked. file_membership . insert ( new_file. clone ( ) ) ;
534+ // propagate the error back to the parent, perhaps it can be handled there
535+ return Err ( e) ;
536+ }
484537 }
485538 }
486539 Ok ( ( ) )
@@ -1734,4 +1787,62 @@ mod test {
17341787 println ! ( "{}\n \n =======================\n {}\n \n " , model_txt, model2_txt) ;
17351788 assert_eq ! ( model_txt, model2_txt) ;
17361789 }
1790+
1791+ #[ test]
1792+ fn model_merge_2 ( ) {
1793+ // regression test for github issue #30
1794+ const FILEBUF1 : & [ u8 ] = br#"<?xml version="1.0" encoding="UTF-8"?>
1795+ <AUTOSAR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://autosar.org/schema/r4.0" xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_4-3-0.xsd">
1796+ <AR-PACKAGES>
1797+ <AR-PACKAGE>
1798+ <SHORT-NAME>BSWMD_Package</SHORT-NAME>
1799+ <ELEMENTS>
1800+ <BSW-MODULE-DESCRIPTION>
1801+ <SHORT-NAME>BSWMD</SHORT-NAME>
1802+ <IMPLEMENTED-ENTRYS>
1803+ <BSW-MODULE-ENTRY-REF-CONDITIONAL>
1804+ <BSW-MODULE-ENTRY-REF DEST="BSW-MODULE-ENTRY">/path/to/entry_A0</BSW-MODULE-ENTRY-REF>
1805+ </BSW-MODULE-ENTRY-REF-CONDITIONAL>
1806+ </IMPLEMENTED-ENTRYS>
1807+ </BSW-MODULE-DESCRIPTION>
1808+ </ELEMENTS>
1809+ </AR-PACKAGE>
1810+ </AR-PACKAGES>
1811+ </AUTOSAR>"# ;
1812+ const FILEBUF2 : & [ u8 ] = br#"<?xml version="1.0" encoding="UTF-8"?>
1813+ <AUTOSAR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://autosar.org/schema/r4.0" xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_4-3-0.xsd">
1814+ <AR-PACKAGES>
1815+ <AR-PACKAGE>
1816+ <SHORT-NAME>BSWMD_Package</SHORT-NAME>
1817+ <ELEMENTS>
1818+ <BSW-MODULE-DESCRIPTION>
1819+ <SHORT-NAME>BSWMD</SHORT-NAME>
1820+ <IMPLEMENTED-ENTRYS>
1821+ <BSW-MODULE-ENTRY-REF-CONDITIONAL>
1822+ <BSW-MODULE-ENTRY-REF DEST="BSW-MODULE-ENTRY">/path/to/entry_B0</BSW-MODULE-ENTRY-REF>
1823+ </BSW-MODULE-ENTRY-REF-CONDITIONAL>
1824+ <BSW-MODULE-ENTRY-REF-CONDITIONAL>
1825+ <BSW-MODULE-ENTRY-REF DEST="BSW-MODULE-ENTRY">/path/to/entry_B1</BSW-MODULE-ENTRY-REF>
1826+ </BSW-MODULE-ENTRY-REF-CONDITIONAL>
1827+ </IMPLEMENTED-ENTRYS>
1828+ </BSW-MODULE-DESCRIPTION>
1829+ </ELEMENTS>
1830+ </AR-PACKAGE>
1831+ </AR-PACKAGES>
1832+ </AUTOSAR>"# ;
1833+
1834+ let model = AutosarModel :: new ( ) ;
1835+ let ( _, _) = model. load_buffer ( FILEBUF1 , "file1" , true ) . unwrap ( ) ;
1836+ let ( _, _) = model. load_buffer ( FILEBUF2 , "file2" , true ) . unwrap ( ) ;
1837+
1838+ let a0_refs = model. get_references_to ( "/path/to/entry_A0" ) ;
1839+ assert ! ( a0_refs. len( ) == 1 ) ;
1840+ assert ! ( a0_refs[ 0 ] . upgrade( ) . is_some( ) ) ;
1841+ let b0_refs = model. get_references_to ( "/path/to/entry_B0" ) ;
1842+ assert ! ( b0_refs. len( ) == 1 ) ;
1843+ assert ! ( b0_refs[ 0 ] . upgrade( ) . is_some( ) ) ;
1844+ let b1_refs = model. get_references_to ( "/path/to/entry_B1" ) ;
1845+ assert ! ( b1_refs. len( ) == 1 ) ;
1846+ assert ! ( b1_refs[ 0 ] . upgrade( ) . is_some( ) ) ;
1847+ }
17371848}
0 commit comments