From e788f021f7e48951c8d807fd5cf874246165d8e4 Mon Sep 17 00:00:00 2001 From: Daniel Thaler Date: Tue, 1 Aug 2023 23:59:03 +0200 Subject: [PATCH] complete rework of multi-file handling Now it works in all known cases. --- .../src/autosarversion.rs | 2 +- autosar-data-specification/src/lib.rs | 71 +- autosar-data-specification/src/regex.rs | 318 ++++---- autosar-data/examples/businfo/main.rs | 66 +- autosar-data/examples/demo/main.rs | 49 +- autosar-data/examples/generate_files/main.rs | 35 +- autosar-data/examples/sort/main.rs | 4 +- autosar-data/src/arxmlfile.rs | 250 +++--- autosar-data/src/autosarproject.rs | 566 ++++++++++++- autosar-data/src/chardata.rs | 135 ++-- autosar-data/src/element.rs | 752 ++++++++++++------ autosar-data/src/iterators.rs | 95 ++- autosar-data/src/lexer.rs | 25 + autosar-data/src/lib.rs | 35 +- autosar-data/src/parser.rs | 11 +- 15 files changed, 1711 insertions(+), 703 deletions(-) diff --git a/autosar-data-specification/src/autosarversion.rs b/autosar-data-specification/src/autosarversion.rs index 29a0144..c1264d3 100644 --- a/autosar-data-specification/src/autosarversion.rs +++ b/autosar-data-specification/src/autosarversion.rs @@ -4,7 +4,7 @@ pub struct ParseAutosarVersionError; #[allow(non_camel_case_types)] -#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)] #[repr(u32)] /// Enum of all Autosar versions pub enum AutosarVersion { diff --git a/autosar-data-specification/src/lib.rs b/autosar-data-specification/src/lib.rs index 98ada1e..0ac8346 100644 --- a/autosar-data-specification/src/lib.rs +++ b/autosar-data-specification/src/lib.rs @@ -443,12 +443,19 @@ impl ElementType { /// Is the current ElementType splittable /// - /// This function returns a bitfiled that indicates in which versions (if any) the ElementType is marked as splittable. + /// This function returns a bitfield that indicates in which versions (if any) the ElementType is marked as splittable. /// A splittable element may be split across multiple arxml files - pub fn splitable(&self) -> u32 { + pub fn splittable(&self) -> u32 { DATATYPES[self.0].splitable } + /// Is the current ElementType splittable in the given version + /// + /// A splittable element may be split across multiple arxml files + pub fn splittable_in(&self, version: AutosarVersion) -> bool { + (DATATYPES[self.0].splitable & (version as u32)) != 0 + } + /// ElementType::ROOT is the root ElementType of the Autosar arxml document, i.e. this is the ElementType of the AUTOSAR element pub const ROOT: Self = ElementType(ROOT_DATATYPE); } @@ -721,7 +728,7 @@ mod test { let spec_dbgstr = format!("{:#?}", spec); assert!(!spec_dbgstr.is_empty()); // xmlns in AUTOSAR is required - assert_eq!(required, true); + assert!(required); // must be specified both in the first and latest versions (and every one in between - not tested) assert_ne!(version & AutosarVersion::Autosar_00050 as u32, 0); assert_ne!(version & AutosarVersion::Autosar_4_0_1 as u32, 0); @@ -880,7 +887,7 @@ mod test { ); // clone impl exists - let cloned = AutosarVersion::Autosar_00050.clone(); + let cloned = AutosarVersion::Autosar_00050; assert_eq!(cloned, AutosarVersion::Autosar_00050); // version parse error @@ -904,7 +911,7 @@ mod test { assert_eq!(AttributeName::Uuid.to_string(), "UUID"); // clone impl exists - let cloned = AttributeName::Uuid.clone(); + let cloned = AttributeName::Uuid; assert_eq!(cloned, AttributeName::Uuid); // attribute parse error @@ -928,7 +935,7 @@ mod test { assert_eq!(ElementName::Autosar.to_string(), "AUTOSAR"); // clone impl exists - let cloned = ElementName::Autosar.clone(); + let cloned = ElementName::Autosar; assert_eq!(cloned, ElementName::Autosar); // element name parse error @@ -952,7 +959,7 @@ mod test { assert_eq!(EnumItem::Default.to_string(), "DEFAULT"); // clone impl exists - let cloned = EnumItem::Abstract.clone(); + let cloned = EnumItem::Abstract; assert_eq!(cloned, EnumItem::Abstract); // enum item parse error @@ -989,7 +996,7 @@ mod test { } #[test] - fn splitable() { + fn splittable() { let (ar_packages_type, _) = ElementType::ROOT .find_sub_element(ElementName::ArPackages, u32::MAX) .unwrap(); @@ -1000,7 +1007,51 @@ mod test { .find_sub_element(ElementName::Elements, u32::MAX) .unwrap(); - assert_ne!(ar_packages_type.splitable() & AutosarVersion::Autosar_00051 as u32, 0); - assert_ne!(elements_type.splitable() & AutosarVersion::Autosar_00051 as u32, 0); + assert!(!ar_package_type.splittable_in(AutosarVersion::Autosar_00051)); + assert_ne!(ar_packages_type.splittable() & AutosarVersion::Autosar_00051 as u32, 0); + assert!(ar_packages_type.splittable_in(AutosarVersion::Autosar_00051)); + assert_ne!(elements_type.splittable() & AutosarVersion::Autosar_00051 as u32, 0); + } + + #[test] + fn traits() { + // this test is basically nonsense - derived traits should all be ok + // but there is no way to exclude them from coverage + // ElementMultiplicity: Debug & Clone + let mult = ElementMultiplicity::Any; + let m2 = mult.clone(); // must be .clone(), otherwise the copy impl is tested instead + assert_eq!(format!("{:#?}", mult), format!("{:#?}", m2)); + + // ContentMode: Debug, Clone + let cm = ContentMode::Sequence; + let cm2 = cm.clone(); // must be .clone(), otherwise the copy impl is tested instead + assert_eq!(format!("{:#?}", cm), format!("{:#?}", cm2)); + + // ElementType: Debug, Clone, Eq & Hash + let et = ElementType::ROOT; + let et2 = et.clone(); // must be .clone(), otherwise the copy impl is tested instead + assert_eq!(format!("{:#?}", et), format!("{:#?}", et2)); + let mut hashset = HashSet::::new(); + hashset.insert(et); + let inserted = hashset.insert(et2); + assert!(!inserted); + + // AutosarVersion: Debug, Clone, Hash + let ver = AutosarVersion::LATEST; + let ver2 = ver.clone(); // must be .clone(), otherwise the copy impl is tested instead + assert_eq!(format!("{ver:#?}"), format!("{ver2:#?}")); + let mut hashset = HashSet::::new(); + hashset.insert(ver); + let inserted = hashset.insert(ver2); + assert!(!inserted); + + // ElementName: Debug, Clone, Hash + let en = ElementName::Autosar; + let en2 = en.clone(); // must be .clone(), otherwise the copy impl is tested instead + assert_eq!(format!("{en:#?}"), format!("{en2:#?}")); + let mut hashset = HashSet::::new(); + hashset.insert(en); + let inserted = hashset.insert(en2); + assert!(!inserted); } } diff --git a/autosar-data-specification/src/regex.rs b/autosar-data-specification/src/regex.rs index 4155b9b..f088468 100644 --- a/autosar-data-specification/src/regex.rs +++ b/autosar-data-specification/src/regex.rs @@ -3246,378 +3246,378 @@ mod test { #[test] fn test_regex_1() { /* regex: ^(0x[0-9a-z]*)$ */ - assert_eq!(validate_regex_1(b""), false); - assert_eq!(validate_regex_1(b"0x1234567890"), true); - assert_eq!(validate_regex_1(b"0xaAbBcCdDeEfF"), true); - assert_eq!(validate_regex_1(b"12345"), false); + assert!(!validate_regex_1(b"")); + assert!(validate_regex_1(b"0x1234567890")); + assert!(validate_regex_1(b"0xaAbBcCdDeEfF")); + assert!(!validate_regex_1(b"12345")); } #[test] fn test_regex_2() { /* regex: ^([1-9][0-9]*|0[xX][0-9a-fA-F]*|0[bB][0-1]+|0[0-7]*|UNSPECIFIED|UNKNOWN|BOOLEAN|PTR)$ */ /* empty string */ - assert_eq!(validate_regex_2(b""), false); + assert!(!validate_regex_2(b"")); /* decimal */ - assert_eq!(validate_regex_2(b"1234567890"), true); + assert!(validate_regex_2(b"1234567890")); /* hex */ - assert_eq!(validate_regex_2(b"0x1234567890abcdefABCDEF"), true); + assert!(validate_regex_2(b"0x1234567890abcdefABCDEF")); /* octal */ - assert_eq!(validate_regex_2(b"01234567"), true); + assert!(validate_regex_2(b"01234567")); /* other */ - assert_eq!(validate_regex_2(b"UNSPECIFIED"), true); - assert_eq!(validate_regex_2(b"UNKNOWN"), true); - assert_eq!(validate_regex_2(b"BOOLEAN"), true); - assert_eq!(validate_regex_2(b"PTR"), true); + assert!(validate_regex_2(b"UNSPECIFIED")); + assert!(validate_regex_2(b"UNKNOWN")); + assert!(validate_regex_2(b"BOOLEAN")); + assert!(validate_regex_2(b"PTR")); /* invalid hex */ - assert_eq!(validate_regex_2(b"0xghij"), false); + assert!(!validate_regex_2(b"0xghij")); /* invalid octal */ - assert_eq!(validate_regex_2(b"08"), false); + assert!(!validate_regex_2(b"08")); /* invalid other */ - assert_eq!(validate_regex_2(b"hello world"), false); + assert!(!validate_regex_2(b"hello world")); } #[test] fn test_regex_3() { /* regex: ^([1-9][0-9]*|0[xX][0-9a-fA-F]+|0[0-7]*|0[bB][0-1]+|ANY|ALL)$ */ /* empty string */ - assert_eq!(validate_regex_3(b""), false); + assert!(!validate_regex_3(b"")); /* decimal */ - assert_eq!(validate_regex_3(b"1234567890"), true); + assert!(validate_regex_3(b"1234567890")); /* hex */ - assert_eq!(validate_regex_3(b"0x1234567890abcdefABCDEF"), true); + assert!(validate_regex_3(b"0x1234567890abcdefABCDEF")); /* octal */ - assert_eq!(validate_regex_3(b"01234567"), true); + assert!(validate_regex_3(b"01234567")); /* other */ - assert_eq!(validate_regex_3(b"ANY"), true); - assert_eq!(validate_regex_3(b"ALL"), true); + assert!(validate_regex_3(b"ANY")); + assert!(validate_regex_3(b"ALL")); /* invalid hex */ - assert_eq!(validate_regex_3(b"0xghij"), false); + assert!(!validate_regex_3(b"0xghij")); /* invalid octal */ - assert_eq!(validate_regex_3(b"08"), false); + assert!(!validate_regex_3(b"08")); /* invalid other */ - assert_eq!(validate_regex_3(b"hello world"), false); + assert!(!validate_regex_3(b"hello world")); } #[test] fn test_regex_4() { /* regex: ^([0-9]+|ANY)$ */ /* matching: */ - assert_eq!(validate_regex_4(b"ANY"), true); - assert_eq!(validate_regex_4(b"1234567890"), true); - assert_eq!(validate_regex_4(b"0123456789"), true); + assert!(validate_regex_4(b"ANY")); + assert!(validate_regex_4(b"1234567890")); + assert!(validate_regex_4(b"0123456789")); /* non-matching */ - assert_eq!(validate_regex_4(b""), false); - assert_eq!(validate_regex_4(b"hello world"), false); + assert!(!validate_regex_4(b"")); + assert!(!validate_regex_4(b"hello world")); } #[test] fn test_regex_5() { /* regex ^([0-9]+|STRING|ARRAY)$ */ /* matching: */ - assert_eq!(validate_regex_5(b"STRING"), true); - assert_eq!(validate_regex_5(b"ARRAY"), true); - assert_eq!(validate_regex_5(b"1234567890"), true); - assert_eq!(validate_regex_5(b"0123456789"), true); + assert!(validate_regex_5(b"STRING")); + assert!(validate_regex_5(b"ARRAY")); + assert!(validate_regex_5(b"1234567890")); + assert!(validate_regex_5(b"0123456789")); /* non-matching */ - assert_eq!(validate_regex_5(b""), false); - assert_eq!(validate_regex_5(b"hello world"), false); + assert!(!validate_regex_5(b"")); + assert!(!validate_regex_5(b"hello world")); } #[test] fn test_regex_6() { /* regex ^(0|1|true|false)$ */ /* matching */ - assert_eq!(validate_regex_6(b"0"), true); - assert_eq!(validate_regex_6(b"1"), true); - assert_eq!(validate_regex_6(b"true"), true); - assert_eq!(validate_regex_6(b"false"), true); + assert!(validate_regex_6(b"0")); + assert!(validate_regex_6(b"1")); + assert!(validate_regex_6(b"true")); + assert!(validate_regex_6(b"false")); /* non-matching */ - assert_eq!(validate_regex_6(b""), false); - assert_eq!(validate_regex_6(b"2"), false); - assert_eq!(validate_regex_6(b"hello world"), false); + assert!(!validate_regex_6(b"")); + assert!(!validate_regex_6(b"2")); + assert!(!validate_regex_6(b"hello world")); } #[test] fn test_regex_7() { /* regex ^([a-zA-Z_][a-zA-Z0-9_]*)$ */ /* matching */ - assert_eq!(validate_regex_7(b"Text_0"), true); - assert_eq!(validate_regex_7(b"_Text_1"), true); - assert_eq!(validate_regex_7(b"TEXT_9"), true); + assert!(validate_regex_7(b"Text_0")); + assert!(validate_regex_7(b"_Text_1")); + assert!(validate_regex_7(b"TEXT_9")); /* non-matching */ - assert_eq!(validate_regex_7(b""), false); - assert_eq!(validate_regex_7(b"0Text_0"), false); - assert_eq!(validate_regex_7(b"Text Text"), false); + assert!(!validate_regex_7(b"")); + assert!(!validate_regex_7(b"0Text_0")); + assert!(!validate_regex_7(b"Text Text")); } #[test] fn test_regex_8() { /* regex ^([a-zA-Z][a-zA-Z0-9_]*)$ */ /* matching */ - assert_eq!(validate_regex_8(b"Text_0"), true); - assert_eq!(validate_regex_8(b"TEXT_9"), true); + assert!(validate_regex_8(b"Text_0")); + assert!(validate_regex_8(b"TEXT_9")); /* non-matching */ - assert_eq!(validate_regex_8(b""), false); - assert_eq!(validate_regex_8(b"_Text_1"), false); - assert_eq!(validate_regex_8(b"0Text_0"), false); - assert_eq!(validate_regex_8(b"Text Text"), false); + assert!(!validate_regex_8(b"")); + assert!(!validate_regex_8(b"_Text_1")); + assert!(!validate_regex_8(b"0Text_0")); + assert!(!validate_regex_8(b"Text Text")); } #[test] fn test_regex_9() { /* regex ^(([0-9]{4}-[0-9]{2}-[0-9]{2})(T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|([+\-][0-9]{2}:[0-9]{2})))?)$ */ /* matching */ - assert_eq!(validate_regex_9(b"2022-01-01T12:00:00Z"), true); - assert_eq!(validate_regex_9(b"2022-01-01T12:00:00+01:30"), true); + assert!(validate_regex_9(b"2022-01-01T12:00:00Z")); + assert!(validate_regex_9(b"2022-01-01T12:00:00+01:30")); /* non-matching */ - assert_eq!(validate_regex_9(b""), false); - assert_eq!(validate_regex_9(b"202-01-01T12:00:00Z"), false); + assert!(!validate_regex_9(b"")); + assert!(!validate_regex_9(b"202-01-01T12:00:00Z")); } #[test] fn test_regex_10() { /* regex ^([a-zA-Z][a-zA-Z0-9-]*)$ */ /* matching */ - assert_eq!(validate_regex_10(b"Text-0"), true); - assert_eq!(validate_regex_10(b"TEXT-9"), true); + assert!(validate_regex_10(b"Text-0")); + assert!(validate_regex_10(b"TEXT-9")); /* non-matching */ - assert_eq!(validate_regex_10(b""), false); - assert_eq!(validate_regex_10(b"-"), false); - assert_eq!(validate_regex_10(b"-Text-1"), false); - assert_eq!(validate_regex_10(b"0Text_0"), false); - assert_eq!(validate_regex_10(b"Text Text"), false); + assert!(!validate_regex_10(b"")); + assert!(!validate_regex_10(b"-")); + assert!(!validate_regex_10(b"-Text-1")); + assert!(!validate_regex_10(b"0Text_0")); + assert!(!validate_regex_10(b"Text Text")); } #[test] fn test_regex_11() { /* regex ^([0-9a-zA-Z_\-]+)$ */ /* matching */ - assert_eq!(validate_regex_11(b"Text-0"), true); - assert_eq!(validate_regex_11(b"TEXT-9"), true); - assert_eq!(validate_regex_11(b"-"), true); - assert_eq!(validate_regex_11(b"_"), true); + assert!(validate_regex_11(b"Text-0")); + assert!(validate_regex_11(b"TEXT-9")); + assert!(validate_regex_11(b"-")); + assert!(validate_regex_11(b"_")); /* non-matching */ - assert_eq!(validate_regex_11(b""), false); - assert_eq!(validate_regex_11(b"hello world"), false); + assert!(!validate_regex_11(b"")); + assert!(!validate_regex_11(b"hello world")); } #[test] fn test_regex_12() { /* regex ^(%[ \-+#]?[0-9]*(\.[0-9]+)?[diouxXfeEgGcs])$ */ /* matching */ - assert_eq!(validate_regex_12(b"%B"), true); - assert_eq!(validate_regex_12(b"%d"), true); - assert_eq!(validate_regex_12(b"% 9.9f"), true); - assert_eq!(validate_regex_12(b"%s"), true); - assert_eq!(validate_regex_12(b"%#.9X"), true); + assert!(validate_regex_12(b"%B")); + assert!(validate_regex_12(b"%d")); + assert!(validate_regex_12(b"% 9.9f")); + assert!(validate_regex_12(b"%s")); + assert!(validate_regex_12(b"%#.9X")); /* non-matching */ - assert_eq!(validate_regex_12(b""), false); - assert_eq!(validate_regex_12(b"d"), false); + assert!(!validate_regex_12(b"")); + assert!(!validate_regex_12(b"d")); } #[test] fn test_regex_13() { /* regex ^(0|[\+\-]?[1-9][0-9]*|0[xX][0-9a-fA-F]+|0[bB][0-1]+|0[0-7]+)$ */ /* matching */ - assert_eq!(validate_regex_13(b"0"), true); - assert_eq!(validate_regex_13(b"-19"), true); - assert_eq!(validate_regex_13(b"0XDEADBEEF"), true); - assert_eq!(validate_regex_13(b"0b010101"), true); + assert!(validate_regex_13(b"0")); + assert!(validate_regex_13(b"-19")); + assert!(validate_regex_13(b"0XDEADBEEF")); + assert!(validate_regex_13(b"0b010101")); /* non-matching */ - assert_eq!(validate_regex_13(b""), false); - assert_eq!(validate_regex_13(b"-019"), false); - assert_eq!(validate_regex_13(b"0XDEADBEEG"), false); - assert_eq!(validate_regex_13(b"0b010102"), false); + assert!(!validate_regex_13(b"")); + assert!(!validate_regex_13(b"-019")); + assert!(!validate_regex_13(b"0XDEADBEEG")); + assert!(!validate_regex_13(b"0b010102")); } #[test] fn test_regex_14() { /* regex ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|ANY)$ */ /* matching */ - assert_eq!(validate_regex_14(b"192.168.0.1"), true); - assert_eq!(validate_regex_14(b"255.255.255.0"), true); - assert_eq!(validate_regex_14(b"ANY"), true); + assert!(validate_regex_14(b"192.168.0.1")); + assert!(validate_regex_14(b"255.255.255.0")); + assert!(validate_regex_14(b"ANY")); /* non-matching */ - assert_eq!(validate_regex_14(b""), false); - assert_eq!(validate_regex_14(b"255.255.255.255.255"), false); + assert!(!validate_regex_14(b"")); + assert!(!validate_regex_14(b"255.255.255.255.255")); } #[test] fn test_regex_15() { /* regex ^([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){7,7}|ANY)$ */ /* matching */ - assert_eq!(validate_regex_15(b"fe80:0:abcd:1234:0:0:0:1"), true); - assert_eq!(validate_regex_15(b"ANY"), true); + assert!(validate_regex_15(b"fe80:0:abcd:1234:0:0:0:1")); + assert!(validate_regex_15(b"ANY")); /* non-matching */ - assert_eq!(validate_regex_15(b""), false); - assert_eq!(validate_regex_15(b"fe80::abcd:1234::::1"), false); + assert!(!validate_regex_15(b"")); + assert!(!validate_regex_15(b"fe80::abcd:1234::::1")); } #[test] fn test_regex_16() { /* regex ^((0[xX][0-9a-fA-F]+)|(0[0-7]+)|(0[bB][0-1]+)|(([+\-]?[1-9][0-9]+(\.[0-9]+)?|[+\-]?[0-9](\.[0-9]+)?)([eE]([+\-]?)[0-9]+)?)|\.0|INF|-INF|NaN)$ */ /* matching */ - assert_eq!(validate_regex_16(b"0xC0"), true); - assert_eq!(validate_regex_16(b"0777"), true); - assert_eq!(validate_regex_16(b"+1234"), true); - assert_eq!(validate_regex_16(b"-3.1415e-42"), true); - assert_eq!(validate_regex_16(b"INF"), true); - assert_eq!(validate_regex_16(b"NaN"), true); + assert!(validate_regex_16(b"0xC0")); + assert!(validate_regex_16(b"0777")); + assert!(validate_regex_16(b"+1234")); + assert!(validate_regex_16(b"-3.1415e-42")); + assert!(validate_regex_16(b"INF")); + assert!(validate_regex_16(b"NaN")); /* non-matching */ - assert_eq!(validate_regex_16(b""), false); - assert_eq!(validate_regex_16(b"text"), false); + assert!(!validate_regex_16(b"")); + assert!(!validate_regex_16(b"text")); } #[test] fn test_regex_17() { /* regex ^(([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2})$ */ /* matching */ - assert_eq!(validate_regex_17(b"0A:1B:2C:3D:4E:5F"), true); + assert!(validate_regex_17(b"0A:1B:2C:3D:4E:5F")); /* non-matching */ - assert_eq!(validate_regex_17(b""), false); - assert_eq!(validate_regex_17(b"0A:1B:2C:3D:4E"), false); + assert!(!validate_regex_17(b"")); + assert!(!validate_regex_17(b"0A:1B:2C:3D:4E")); } #[test] fn test_regex_18() { /* regex ^([a-zA-Z_][a-zA-Z0-9_]*(\[([a-zA-Z_][a-zA-Z0-9_]*|[0-9]+)\])*(\.[a-zA-Z_][a-zA-Z0-9_]*(\[([a-zA-Z_][a-zA-Z0-9_]*|[0-9]+)\])*)*)$ */ /* matching */ - assert_eq!(validate_regex_18(b"aabb9_cd[x][y].cde"), true); + assert!(validate_regex_18(b"aabb9_cd[x][y].cde")); /* non-matching */ - assert_eq!(validate_regex_18(b""), false); - assert_eq!(validate_regex_18(b"42"), false); + assert!(!validate_regex_18(b"")); + assert!(!validate_regex_18(b"42")); } #[test] fn test_regex_19() { /* regex ^([A-Z][a-zA-Z0-9_]*)$ */ /* matching */ - assert_eq!(validate_regex_19(b"Text_Text"), true); + assert!(validate_regex_19(b"Text_Text")); /* non-matching */ - assert_eq!(validate_regex_19(b""), false); - assert_eq!(validate_regex_19(b"text"), false); - assert_eq!(validate_regex_19(b"Text Text"), false); + assert!(!validate_regex_19(b"")); + assert!(!validate_regex_19(b"text")); + assert!(!validate_regex_19(b"Text Text")); } #[test] fn test_regex_20() { /* regex ^([1-9][0-9]*)$ */ /* matching */ - assert_eq!(validate_regex_20(b"123"), true); + assert!(validate_regex_20(b"123")); /* non-matching */ - assert_eq!(validate_regex_20(b""), false); - assert_eq!(validate_regex_20(b"abcd"), false); - assert_eq!(validate_regex_20(b"0x123"), false); + assert!(!validate_regex_20(b"")); + assert!(!validate_regex_20(b"abcd")); + assert!(!validate_regex_20(b"0x123")); } #[test] fn test_regex_21() { /* regex ^(0|[\+]?[1-9][0-9]*|0[xX][0-9a-fA-F]+|0[bB][0-1]+|0[0-7]+)$ */ /* matching */ - assert_eq!(validate_regex_21(b"0"), true); - assert_eq!(validate_regex_21(b"+19"), true); - assert_eq!(validate_regex_21(b"0xbadcafe"), true); - assert_eq!(validate_regex_21(b"0b1010"), true); - assert_eq!(validate_regex_21(b"0777"), true); + assert!(validate_regex_21(b"0")); + assert!(validate_regex_21(b"+19")); + assert!(validate_regex_21(b"0xbadcafe")); + assert!(validate_regex_21(b"0b1010")); + assert!(validate_regex_21(b"0777")); /* non-matching */ - assert_eq!(validate_regex_21(b""), false); - assert_eq!(validate_regex_21(b"-19"), false); - assert_eq!(validate_regex_21(b"1.23"), false); - assert_eq!(validate_regex_21(b"text"), false); + assert!(!validate_regex_21(b"")); + assert!(!validate_regex_21(b"-19")); + assert!(!validate_regex_21(b"1.23")); + assert!(!validate_regex_21(b"text")); } #[test] fn test_regex_22() { /* regex ^([a-zA-Z]([a-zA-Z0-9]|_[a-zA-Z0-9])*_?)$ */ /* matching */ - assert_eq!(validate_regex_22(b"text"), true); - assert_eq!(validate_regex_22(b"text_text"), true); + assert!(validate_regex_22(b"text")); + assert!(validate_regex_22(b"text_text")); /* non-matching */ - assert_eq!(validate_regex_22(b""), false); - assert_eq!(validate_regex_22(b"_text"), false); - assert_eq!(validate_regex_22(b"text__text"), false); + assert!(!validate_regex_22(b"")); + assert!(!validate_regex_22(b"_text")); + assert!(!validate_regex_22(b"text__text")); } #[test] fn test_regex_23() { /* regex ^(-?([0-9]+|MAX-TEXT-SIZE|ARRAY-SIZE))$ */ /* matching */ - assert_eq!(validate_regex_23(b"-000"), true); - assert_eq!(validate_regex_23(b"33"), true); - assert_eq!(validate_regex_23(b"MAX-TEXT-SIZE"), true); - assert_eq!(validate_regex_23(b"ARRAY-SIZE"), true); + assert!(validate_regex_23(b"-000")); + assert!(validate_regex_23(b"33")); + assert!(validate_regex_23(b"MAX-TEXT-SIZE")); + assert!(validate_regex_23(b"ARRAY-SIZE")); /* non-matching */ - assert_eq!(validate_regex_23(b""), false); - assert_eq!(validate_regex_23(b"text"), false); - assert_eq!(validate_regex_23(b"1.23"), false); + assert!(!validate_regex_23(b"")); + assert!(!validate_regex_23(b"text")); + assert!(!validate_regex_23(b"1.23")); } #[test] fn test_regex_24() { /* regex ^(/?[a-zA-Z][a-zA-Z0-9_]{0,127}(/[a-zA-Z][a-zA-Z0-9_]{0,127})*)$ */ /* matching */ - assert_eq!(validate_regex_24(b"/path/to/element"), true); - assert_eq!(validate_regex_24(b"element_name"), true); + assert!(validate_regex_24(b"/path/to/element")); + assert!(validate_regex_24(b"element_name")); /* non-matching */ - assert_eq!(validate_regex_24(b""), false); - assert_eq!(validate_regex_24(b"1234"), false); + assert!(!validate_regex_24(b"")); + assert!(!validate_regex_24(b"1234")); } #[test] fn test_regex_25() { /* regex ^([0-9]+\.[0-9]+\.[0-9]+([\._;].*)?)$ */ /* matching */ - assert_eq!(validate_regex_25(b"0.1.2_text"), true); + assert!(validate_regex_25(b"0.1.2_text")); /* non-matching */ - assert_eq!(validate_regex_25(b""), false); - assert_eq!(validate_regex_25(b"text"), false); - assert_eq!(validate_regex_25(b"12"), false); + assert!(!validate_regex_25(b"")); + assert!(!validate_regex_25(b"text")); + assert!(!validate_regex_25(b"12")); } #[test] fn test_regex_26() { /* regex ^((0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-((0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?)$ */ /* matching */ - assert_eq!(validate_regex_26(b"0.0.0-ab-c.0.0+zz-Z"), true); + assert!(validate_regex_26(b"0.0.0-ab-c.0.0+zz-Z")); /* non-matching */ - assert_eq!(validate_regex_26(b""), false); - assert_eq!(validate_regex_26(b"0"), false); - assert_eq!(validate_regex_26(b"text"), false); + assert!(!validate_regex_26(b"")); + assert!(!validate_regex_26(b"0")); + assert!(!validate_regex_26(b"text")); } #[test] fn test_regex_27() { /* regex ^([0-1])$ */ /* matching */ - assert_eq!(validate_regex_27(b"0"), true); - assert_eq!(validate_regex_27(b"1"), true); + assert!(validate_regex_27(b"0")); + assert!(validate_regex_27(b"1")); /* non-matching */ - assert_eq!(validate_regex_27(b""), false); - assert_eq!(validate_regex_27(b"text"), false); - assert_eq!(validate_regex_27(b"10"), false); + assert!(!validate_regex_27(b"")); + assert!(!validate_regex_27(b"text")); + assert!(!validate_regex_27(b"10")); } #[test] fn test_regex_28() { /* regex ^((-?[a-zA-Z_]+)(( )+-?[a-zA-Z_]+)*)$ */ /* matching */ - assert_eq!(validate_regex_28(b"-text_text-Z__Z"), false); + assert!(!validate_regex_28(b"-text_text-Z__Z")); /* non-matching */ - assert_eq!(validate_regex_28(b""), false); - assert_eq!(validate_regex_28(b"--"), false); - assert_eq!(validate_regex_28(b"1"), false); + assert!(!validate_regex_28(b"")); + assert!(!validate_regex_28(b"--")); + assert!(!validate_regex_28(b"1")); } } diff --git a/autosar-data/examples/businfo/main.rs b/autosar-data/examples/businfo/main.rs index 89b8f24..9163a03 100644 --- a/autosar-data/examples/businfo/main.rs +++ b/autosar-data/examples/businfo/main.rs @@ -269,7 +269,7 @@ fn display_can_ft(frame_triggering: &Element) -> Option<()> { if ft_name != frame_name { print!(" (frame triggering: {ft_name})"); } - println!(""); + println!(); if !sender_ecus.is_empty() { println!(" Senders: {}", sender_ecus.join(", ")); } @@ -308,7 +308,7 @@ fn display_can_ft(frame_triggering: &Element) -> Option<()> { display_mapped_pdu(&pdu_mapping); } } - println!(""); + println!(); Some(()) } @@ -489,7 +489,7 @@ fn display_flexray_ft(frame_triggering: &Element) -> Option<()> { if ft_name != frame_name { print!(" (frame triggering: {ft_name})"); } - println!(""); + println!(); if !sender_ecus.is_empty() { println!(" Senders: {}", sender_ecus.join(", ")); } @@ -522,7 +522,7 @@ fn display_flexray_ft(frame_triggering: &Element) -> Option<()> { display_mapped_pdu(&pdu_mapping); } } - println!(""); + println!(); Some(()) } @@ -883,7 +883,7 @@ fn display_isignal_ipdu(pdu: &Element, indent: usize) { if let Some(length) = length { print!(", length: {length} bit"); } - println!(""); + println!(); } } } @@ -903,15 +903,12 @@ fn get_time_range(base: &Element) -> Option { .and_then(|cdata| cdata.double_value()) { Some(TimeRangeTolerance::Absolute(absolute_tolerance)) - } else if let Some(relative_tolerance) = base - .get_sub_element(ElementName::RelativeTolerance) - .and_then(|elem| elem.get_sub_element(ElementName::Relative)) - .and_then(|elem| elem.character_data()) - .and_then(|cdata| decode_integer(&cdata)) - { - Some(TimeRangeTolerance::Relative(relative_tolerance)) } else { - None + base.get_sub_element(ElementName::RelativeTolerance) + .and_then(|elem| elem.get_sub_element(ElementName::Relative)) + .and_then(|elem| elem.character_data()) + .and_then(|cdata| decode_integer(&cdata)) + .map(TimeRangeTolerance::Relative) }; Some(TimeRange { tolerance, value }) @@ -977,7 +974,7 @@ fn display_isignal_group( if let Some(length) = length { print!(", length: {length} bit"); } - println!(""); + println!(); } // show minimal info about any data transformation attached to the group if let Some(com_transformations) = signal_group.get_sub_element(ElementName::ComBasedSignalGroupTransformations) { @@ -1086,7 +1083,7 @@ fn display_nm_pdu(pdu: &Element, indent: usize) { { print!(", length: {length} bit"); } - println!(""); + println!(); } } } @@ -1225,7 +1222,7 @@ fn display_ethernet_channel(channel: &Element) -> Option<()> { .get_sub_element(ElementName::VlanIdentifier) .and_then(|vlan_id| vlan_id.character_data()) { - println!(" Identifier: {}", vlan_identifier.to_string()); + println!(" Identifier: {}", vlan_identifier); } println!(); } @@ -1409,8 +1406,7 @@ fn display_soad_connection_bundles(connection_bundles: Element) { let ip_port = server_socket .get_sub_element(ElementName::ApplicationEndpoint) .and_then(|ss_ae| get_socket_address_summary(&ss_ae)) - .or(Some("".to_string())) - .unwrap(); + .unwrap_or("".to_string()); print!( " Server socket: {} ({})", server_socket.item_name().unwrap(), @@ -1451,8 +1447,7 @@ fn display_bundled_connection(socket_connection: Element) -> Option<()> { let ip_port = client_socket .get_sub_element(ElementName::ApplicationEndpoint) .and_then(|cs_ae| get_socket_address_summary(&cs_ae)) - .or(Some("dynamic".to_string())) - .unwrap(); + .unwrap_or("dynamic".to_string()); println!(" Socket connection:"); print!(" Client: {} ({ip_port})", client_socket.item_name().unwrap()); if let Some(ecu_instance) = get_socket_ecu(&client_socket) { @@ -1472,7 +1467,7 @@ fn display_bundled_connection(socket_connection: Element) -> Option<()> { if let Some(header_id) = socket_connection_ipdu_identifier .get_sub_element(ElementName::HeaderId) .and_then(|header_id_elem| header_id_elem.character_data()) - .and_then(|cdata| Some(cdata.to_string())) + .map(|cdata| cdata.to_string()) { println!(" Header id: {header_id}"); } @@ -1546,7 +1541,11 @@ fn display_socket_addresses(socket_addresses: Element) { fn display_provided_service_instance(provided_service_instances: &Element) { for psi in provided_service_instances.sub_elements() { println!(" Provided service instance: {}", psi.item_name().unwrap()); - if let Some(service_identifier) = psi.get_sub_element(ElementName::ServiceIdentifier).and_then(|sid| sid.character_data()).and_then(|cdata| Some(cdata.to_string())) { + if let Some(service_identifier) = psi + .get_sub_element(ElementName::ServiceIdentifier) + .and_then(|sid| sid.character_data()) + .map(|cdata| cdata.to_string()) + { println!(" Service identifier: {service_identifier}"); } if let Some(event_handlers) = psi.get_sub_element(ElementName::EventHandlers) { @@ -1643,15 +1642,12 @@ fn get_socket_address_summary(application_endpoint: &Element) -> Option .and_then(|cdata| cdata.string_value()) { Some(format!("[{}]", ip6_addr_str)) - } else if let Some(ip4_addr_str) = network_endpoint_adresses - .get_sub_element(ElementName::Ipv4Configuration) - .and_then(|ipv4_conf| ipv4_conf.get_sub_element(ElementName::Ipv4Address)) - .and_then(|ipv4_addr| ipv4_addr.character_data()) - .and_then(|cdata| cdata.string_value()) - { - Some(ip4_addr_str) } else { - None + network_endpoint_adresses + .get_sub_element(ElementName::Ipv4Configuration) + .and_then(|ipv4_conf| ipv4_conf.get_sub_element(ElementName::Ipv4Address)) + .and_then(|ipv4_addr| ipv4_addr.character_data()) + .and_then(|cdata| cdata.string_value()) } } else { None @@ -1670,10 +1666,8 @@ fn get_socket_address_summary(application_endpoint: &Element) -> Option if let Some(tcp_pn) = tcp_port_number { Some(format!("{}:{} [TCP]", ip_addr_string, tcp_pn)) - } else if let Some(udp_pn) = udp_port_number { - Some(format!("{}:{} [UDP]", ip_addr_string, udp_pn)) } else { - None + udp_port_number.map(|udp_pn| format!("{}:{} [UDP]", ip_addr_string, udp_pn)) } } @@ -1682,7 +1676,7 @@ fn get_socket_address_summary(application_endpoint: &Element) -> Option // true fn get_port_number(tp_port: &Element) -> Option { if let Some(port_number) = tp_port.get_sub_element(ElementName::PortNumber) { - port_number.character_data().and_then(|cdata| Some(cdata.to_string())) + port_number.character_data().map(|cdata| cdata.to_string()) } else { let is_dynamic = tp_port .get_sub_element(ElementName::DynamicallyAssigned) @@ -1732,8 +1726,8 @@ fn decode_integer(cdata: &CharacterData) -> Option { } else if text.starts_with("0B") { let binstr = text.strip_prefix("0B").unwrap(); Some(i64::from_str_radix(binstr, 2).ok()?) - } else if text.starts_with("0") { - let octstr = text.strip_prefix("0").unwrap(); + } else if text.starts_with('0') { + let octstr = text.strip_prefix('0').unwrap(); Some(i64::from_str_radix(octstr, 8).ok()?) } else { Some(text.parse().ok()?) diff --git a/autosar-data/examples/demo/main.rs b/autosar-data/examples/demo/main.rs index 6992cec..dbdc924 100644 --- a/autosar-data/examples/demo/main.rs +++ b/autosar-data/examples/demo/main.rs @@ -6,33 +6,50 @@ use std::{ }; use autosar_data::AutosarProject; +use autosar_data_specification::*; fn main() { let args: Vec = env::args().collect(); - if args.len() != 2 { - println!("Usage: {} ", args[0]); + if args.len() < 2 { + println!("Usage: {} ...", args[0]); return; } let project = AutosarProject::new(); - let filename = OsString::from(&args[1]); - let buffer = match load_file_data(&filename) { - Ok(buffer) => buffer, - Err(error) => { - println!("IO error: {error}"); - return; + for arg in args.iter().skip(1) { + let filename = OsString::from(arg); + let buffer = match load_file_data(&filename) { + Ok(buffer) => buffer, + Err(error) => { + println!("IO error: {error}"); + return; + } + }; + + let now = std::time::Instant::now(); + let result = project.load_named_arxml_buffer(&buffer, &filename, false); + match result { + Ok((_, warnings)) => { + println!("parsing succeeded in {}ms", now.elapsed().as_micros() as f64 / 1000.0); + if !warnings.is_empty() { + for w in warnings { + println!(" Warning: {w}"); + } + } + } + Err(err) => println!("parsing failed: {err}"), } - }; - let now = std::time::Instant::now(); - let result = project.load_named_arxml_buffer(&buffer, &filename, true); - match result { - Ok(_) => println!("parsing succeeded in {}ms", now.elapsed().as_micros() as f64 / 1000.0), - Err(err) => println!("parsing failed: {err}"), + println!("loaded arxml file: {}", arg); } - for file in project.files() { - println!("loaded arxml file: {}", file.filename().to_string_lossy()); + for (_, elem) in project.elements_dfs() { + if elem.is_reference() && elem.element_name() != ElementName::DefinitionRef { + let target_path = elem.character_data().and_then(|cdata| cdata.string_value()).unwrap(); + if project.get_element_by_path(&target_path).is_none() { + println!("Invalid reference from {} to {target_path}", elem.element_name()); + } + } } } diff --git a/autosar-data/examples/generate_files/main.rs b/autosar-data/examples/generate_files/main.rs index dad7546..03aaf1a 100644 --- a/autosar-data/examples/generate_files/main.rs +++ b/autosar-data/examples/generate_files/main.rs @@ -34,7 +34,7 @@ fn main() { let project = AutosarProject::new(); let arxml_file = project.create_file(&filename, version).unwrap(); - let autosar_element = arxml_file.root_element(); + let autosar_element = project.root_element(); let mut counter = 1; create_sub_elements(&autosar_element, &mut counter, &mut completed, version); @@ -47,7 +47,7 @@ fn main() { &format!("http://autosar.org/schema/r4.0 {}", version.filename()), ); - let text = arxml_file.serialize(); + let text = arxml_file.serialize().unwrap(); std::fs::write(&filename, text).unwrap(); } } @@ -65,7 +65,7 @@ fn create_sub_elements( let mut element_complete = true; for (se_name, named, _) in elem.list_valid_sub_elements() { if completed.get(&(elem_name, se_name)).is_none() { - match create_sub_element_helper(&elem, se_name, named, counter) { + match create_sub_element_helper(elem, se_name, named, counter) { Ok(sub_elem) => { any_created = true; if named { @@ -76,23 +76,16 @@ fn create_sub_elements( let (se_complete, _) = create_sub_elements(&sub_elem, counter, completed, version); if !se_complete { completed.remove(&(elem_name, se_name)); - loop { - match create_sub_element_helper(&elem, se_name, named, counter) { - Ok(sub_elem) => { - let (se_complete, se_any_created) = - create_sub_elements(&sub_elem, counter, completed, version); - if se_complete { - break; - } - if !se_any_created { - element_complete = false; - let _ = elem.remove_sub_element(sub_elem); - break; - } - } - Err(_) => { - break; - } + while let Ok(sub_elem) = create_sub_element_helper(elem, se_name, named, counter) { + let (se_complete, se_any_created) = + create_sub_elements(&sub_elem, counter, completed, version); + if se_complete { + break; + } + if !se_any_created { + element_complete = false; + let _ = elem.remove_sub_element(sub_elem); + break; } } } @@ -203,6 +196,6 @@ fn make_cdata(spec: &CharacterDataSpec, version: AutosarVersion) -> CharacterDat CharacterData::String("lorem ipsum".to_string()) } autosar_data_specification::CharacterDataSpec::UnsignedInteger => CharacterData::UnsignedInteger(42), - autosar_data_specification::CharacterDataSpec::Double => CharacterData::Double(3.1415), + autosar_data_specification::CharacterDataSpec::Double => CharacterData::Double(std::f64::consts::PI), } } diff --git a/autosar-data/examples/sort/main.rs b/autosar-data/examples/sort/main.rs index be5c572..cc1245a 100644 --- a/autosar-data/examples/sort/main.rs +++ b/autosar-data/examples/sort/main.rs @@ -26,10 +26,10 @@ fn main() { } }; - arxmlfile.sort(); + project.sort(); // write the sorted file - let filename_prefix = filename.strip_suffix(".arxml").or(Some(filename)).unwrap(); + let filename_prefix = filename.strip_suffix(".arxml").unwrap_or(filename); let new_filename = format!("{filename_prefix}_sorted.arxml"); arxmlfile.set_filename(&new_filename); project.write().unwrap(); diff --git a/autosar-data/src/arxmlfile.rs b/autosar-data/src/arxmlfile.rs index 5073d8d..f6917ad 100644 --- a/autosar-data/src/arxmlfile.rs +++ b/autosar-data/src/arxmlfile.rs @@ -1,44 +1,16 @@ +use std::hash::Hash; use std::path::Path; use crate::*; impl ArxmlFile { pub(crate) fn new>(filename: P, version: AutosarVersion, container: &AutosarProject) -> Self { - let xsi_schemalocation = - CharacterData::String(format!("http://autosar.org/schema/r4.0 {}", version.filename())); - let xmlns = CharacterData::String("http://autosar.org/schema/r4.0".to_string()); - let xmlns_xsi = CharacterData::String("http://www.w3.org/2001/XMLSchema-instance".to_string()); - let root_attributes = smallvec::smallvec![ - Attribute { - attrname: AttributeName::xsiSchemalocation, - content: xsi_schemalocation - }, - Attribute { - attrname: AttributeName::xmlns, - content: xmlns - }, - Attribute { - attrname: AttributeName::xmlnsXsi, - content: xmlns_xsi - }, - ]; - let root_element = Element(Arc::new(Mutex::new(ElementRaw { - parent: ElementOrFile::None, - elemname: ElementName::Autosar, - elemtype: ElementType::ROOT, - content: SmallVec::new(), - attributes: root_attributes, - }))); - let file = Self(Arc::new(Mutex::new(ArxmlFileRaw { - project: container.downgrade(), - root_element, + Self(Arc::new(Mutex::new(ArxmlFileRaw { version, + project: container.downgrade(), filename: filename.as_ref().to_path_buf(), xml_standalone: None, - }))); - let new_parent = ElementOrFile::File(file.downgrade()); - file.root_element().set_parent(new_parent); - file + }))) } /// Get the filename of this ArxmlFile @@ -55,25 +27,6 @@ impl ArxmlFile { self.0.lock().filename.clone() } - /// Set the filename of this arxml filename - /// - /// This will not rename any existing file on disk, but the new filename will be used when writing the data. - /// - /// # Example - /// - /// ``` - /// # use std::path::Path; - /// # use autosar_data::*; - /// # let project = AutosarProject::new(); - /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap(); - /// file.set_filename("foo.arxml"); - /// // or - /// file.set_filename(&Path::new("bar.arxml")); - /// ``` - pub fn set_filename>(&self, new_filename: P) { - self.0.lock().filename = new_filename.as_ref().to_path_buf(); - } - /// Get the [AutosarVersion] of the file /// /// # Example @@ -113,16 +66,16 @@ impl ArxmlFile { if compat_errors.is_empty() { let mut file = self.0.lock(); file.version = new_ver; - let attribute_value = - CharacterData::String(format!("http://autosar.org/schema/r4.0 {}", new_ver.filename())); - let _ = file.root_element.0.lock().set_attribute_internal( - AttributeName::xsiSchemalocation, - attribute_value, - new_ver, - ); + // let attribute_value = + // CharacterData::String(format!("http://autosar.org/schema/r4.0 {}", new_ver.filename())); + // let _ = file.root_element.0.lock().set_attribute_internal( + // AttributeName::xsiSchemalocation, + // attribute_value, + // new_ver, + // ); Ok(()) } else { - Err(AutosarDataError::VersionIncompatible) + Err(AutosarDataError::VersionIncompatibleData { version: new_ver }) } } @@ -140,7 +93,32 @@ impl ArxmlFile { /// let (error_list, compat_mask) = file.check_version_compatibility(AutosarVersion::Autosar_00050); /// ``` pub fn check_version_compatibility(&self, target_version: AutosarVersion) -> (Vec, u32) { - self.root_element().check_version_compatibility(target_version) + if let Ok(project) = self.project() { + project + .root_element() + .check_version_compatibility(&self.downgrade(), target_version) + } else { + (Vec::new(), 0) + } + } + + /// Set the filename of this arxml filename + /// + /// This will not rename any existing file on disk, but the new filename will be used when writing the data. + /// + /// # Example + /// + /// ``` + /// # use std::path::Path; + /// # use autosar_data::*; + /// # let project = AutosarProject::new(); + /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap(); + /// file.set_filename("foo.arxml"); + /// // or + /// file.set_filename(&Path::new("bar.arxml")); + /// ``` + pub fn set_filename>(&self, new_filename: P) { + self.0.lock().filename = new_filename.as_ref().to_path_buf(); } /// Get a reference to the [AutosarProject] object that contains this file @@ -168,23 +146,10 @@ impl ArxmlFile { locked_file.project.upgrade().ok_or(AutosarDataError::ItemDeleted) } - /// Get a referenct to the root `````` element of this file - /// - /// # Example - /// - /// ``` - /// # use autosar_data::*; - /// # let project = AutosarProject::new(); - /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap(); - /// let autosar_element = file.root_element(); - /// ``` - pub fn root_element(&self) -> Element { - let file = self.0.lock(); - file.root_element.clone() - } - /// Create a depth-first search iterator over all [Element]s in this file /// + /// In a multi-file project it will not return any elements from other files. + /// /// # Example /// /// ``` @@ -195,13 +160,16 @@ impl ArxmlFile { /// // ... /// } /// ``` - pub fn elements_dfs(&self) -> ElementsDfsIterator { - let file = self.0.lock(); - file.root_element.elements_dfs() + pub fn elements_dfs(&self) -> ArxmlFileElementsDfsIterator { + ArxmlFileElementsDfsIterator::new(self.downgrade(), &self.project().unwrap().root_element()) } /// Serialize the content of the file to a String /// + /// # Possible errors + /// + /// [AutosarDataError::ItemDeleted]: The project is no longer valid + /// /// # Example /// /// ``` @@ -210,7 +178,7 @@ impl ArxmlFile { /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap(); /// let text = file.serialize(); /// ``` - pub fn serialize(&self) -> String { + pub fn serialize(&self) -> Result { let mut outstring = String::with_capacity(1024 * 1024); match self.xml_standalone() { @@ -218,10 +186,13 @@ impl ArxmlFile { Some(false) => outstring.push_str(""), None => outstring.push_str(""), } + let project = self.project()?; + project.0.lock().set_version(self.0.lock().version); + project + .root_element() + .serialize_internal(&mut outstring, 0, false, Some(self.downgrade())); - self.root_element().serialize_internal(&mut outstring, 0, false); - - outstring + Ok(outstring) } /// Return the standalone attribute from the xml header @@ -246,26 +217,6 @@ impl ArxmlFile { self.0.lock().xml_standalone } - /// Recursively sort all elements in the file. This is exactly identical to calling sort() on the root element of the file. - /// - /// All sub elements of the root element are sorted alphabetically. - /// If the sub-elements are named, then the sorting is performed according to the item names, - /// otherwise the serialized form of the sub-elements is used for sorting. - /// - /// Element attributes are not taken into account while sorting. - /// The elements are sorted in place, and sorting cannot fail, so there is no return value. - /// - /// # Example - /// ``` - /// # use autosar_data::*; - /// # let project = AutosarProject::new(); - /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap(); - /// file.sort(); - /// ``` - pub fn sort(&self) { - self.root_element().sort() - } - /// Create a weak reference to this ArxmlFile /// /// A weak reference can be stored without preventing the file from being deallocated. @@ -292,6 +243,14 @@ impl PartialEq for ArxmlFile { } } +impl Eq for ArxmlFile {} + +impl Hash for ArxmlFile { + fn hash(&self, state: &mut H) { + state.write_usize(Arc::as_ptr(&self.0) as usize); + } +} + impl WeakArxmlFile { /// try to get a strong reference to the [ArxmlFile] /// @@ -307,6 +266,14 @@ impl PartialEq for WeakArxmlFile { } } +impl Eq for WeakArxmlFile {} + +impl Hash for WeakArxmlFile { + fn hash(&self, state: &mut H) { + state.write_usize(Weak::as_ptr(&self.0) as usize); + } +} + #[cfg(test)] mod test { use super::*; @@ -364,11 +331,88 @@ mod test { fn serialize() { let project = AutosarProject::new(); let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap(); - let text = file.serialize(); + assert_eq!(file.project().unwrap(), project); + assert_eq!(project.root_element().element_name(), ElementName::Autosar); + let text = file.serialize().unwrap(); assert_eq!( text, r#" "# ); + file.0.lock().xml_standalone = Some(false); + let text = file.serialize().unwrap(); + assert_eq!( + text, + r#" +"# + ); + file.0.lock().xml_standalone = Some(true); + let text = file.serialize().unwrap(); + assert_eq!( + text, + r#" +"# + ); + } + + #[test] + fn elements_dfs_iterator() { + const FILEBUF_1: &[u8] = r#" + + + + Pkg + + System + + + "#.as_bytes(); + const FILEBUF_2: &[u8] = r#" + + + + Pkg2 + + DataType + + + "#.as_bytes(); + + let project = AutosarProject::new(); + let (file, _) = project + .load_named_arxml_buffer(FILEBUF_1, "file1.arxml", false) + .unwrap(); + let proj_elem_count = project.elements_dfs().count(); + let file_elem_count = file.elements_dfs().count(); + assert_eq!(proj_elem_count, file_elem_count); + project + .load_named_arxml_buffer(FILEBUF_2, "file2.arxml", false) + .unwrap(); + let proj_elem_count_2 = project.elements_dfs().count(); + let file_elem_count_2 = file.elements_dfs().count(); + assert!(proj_elem_count < proj_elem_count_2); + assert_eq!(file_elem_count, file_elem_count_2); + } + + #[test] + fn traits() { + let project = AutosarProject::new(); + let file = project.create_file("filename", AutosarVersion::LATEST).unwrap(); + let weak_file = file.downgrade(); + let file_cloned = file.clone(); + assert_eq!(file, file_cloned); + assert_eq!(format!("{file:#?}"), format!("{file_cloned:#?}")); + let mut hashset = HashSet::::new(); + hashset.insert(file); + let inserted = hashset.insert(file_cloned); + assert!(!inserted); + + let weak_file_cloned = weak_file.clone(); + assert_eq!(weak_file, weak_file_cloned); + assert_eq!(format!("{weak_file:#?}"), format!("{weak_file_cloned:#?}")); + let mut hashset = HashSet::::new(); + hashset.insert(weak_file); + let inserted = hashset.insert(weak_file_cloned); + assert!(!inserted); } } diff --git a/autosar-data/src/autosarproject.rs b/autosar-data/src/autosarproject.rs index d5b1a94..3bc6476 100644 --- a/autosar-data/src/autosarproject.rs +++ b/autosar-data/src/autosarproject.rs @@ -1,4 +1,8 @@ -use std::{borrow::Cow, collections::HashMap, path::Path, str::FromStr}; +use std::{ + collections::{HashMap, HashSet}, + path::Path, + str::FromStr, +}; use crate::*; @@ -15,11 +19,41 @@ impl AutosarProject { /// ``` /// pub fn new() -> AutosarProject { - AutosarProject(Arc::new(Mutex::new(AutosarProjectRaw { + let version = AutosarVersion::LATEST; + let xsi_schemalocation = + CharacterData::String(format!("http://autosar.org/schema/r4.0 {}", version.filename())); + let xmlns = CharacterData::String("http://autosar.org/schema/r4.0".to_string()); + let xmlns_xsi = CharacterData::String("http://www.w3.org/2001/XMLSchema-instance".to_string()); + let root_attributes = smallvec::smallvec![ + Attribute { + attrname: AttributeName::xsiSchemalocation, + content: xsi_schemalocation + }, + Attribute { + attrname: AttributeName::xmlns, + content: xmlns + }, + Attribute { + attrname: AttributeName::xmlnsXsi, + content: xmlns_xsi + }, + ]; + let root_elem = Element(Arc::new(Mutex::new(ElementRaw { + parent: ElementOrProject::None, + elemname: ElementName::Autosar, + elemtype: ElementType::ROOT, + content: SmallVec::new(), + attributes: root_attributes, + file_membership: HashSet::with_capacity(0), + }))); + let project = AutosarProject(Arc::new(Mutex::new(AutosarProjectRaw { files: Vec::new(), identifiables: FxHashMap::default(), reference_origins: FxHashMap::default(), - }))) + root_element: root_elem.clone(), + }))); + root_elem.set_parent(ElementOrProject::Project(project.downgrade())); + project } /// Create a new [ArxmlFile] inside this AutosarData structure @@ -48,6 +82,7 @@ impl AutosarProject { /// # Possible Errors /// /// - [AutosarDataError::DuplicateFilenameError]: The project already contains a file with this filename + /// - [AutosarDataError::VersionMismatch]: The new file cannot be creatd with a version that differs from the version of existing data /// pub fn create_file>( &self, @@ -64,6 +99,7 @@ impl AutosarProject { } let new_file = ArxmlFile::new(filename, version, self); + data.files.push(new_file.clone()); Ok(new_file) } @@ -119,28 +155,44 @@ impl AutosarProject { let mut parser = ArxmlParser::new(filename.clone(), buffer, strict); let root_element = parser.parse_arxml()?; - + let version = parser.get_fileversion(); let arxml_file = ArxmlFile(Arc::new(Mutex::new(ArxmlFileRaw { + version, project: self.downgrade(), - version: parser.get_fileversion(), + // version: parser.get_fileversion(), filename: filename.clone(), - root_element, xml_standalone: parser.get_standalone(), }))); - // graft on the back-link from the root element to the file - let new_parent = ElementOrFile::File(arxml_file.downgrade()); - arxml_file.root_element().set_parent(new_parent); + + if self.0.lock().files.is_empty() { + root_element.set_parent(ElementOrProject::Project(self.downgrade())); + self.0.lock().root_element = root_element; + } else { + let result = self.merge_file_data(&root_element, arxml_file.downgrade()); + if let Err(error) = result { + self.unmerge_file(&arxml_file.downgrade()); + return Err(error); + } + } let mut data = self.0.lock(); data.identifiables.reserve(parser.identifiables.len()); for (key, value) in parser.identifiables { - if let Some(existing) = data.identifiables.insert(key, value) { - if let Some(element) = existing.upgrade() { - return Err(AutosarDataError::OverlappingDataError { - filename, - path: element.path().unwrap_or_else(|_| "".to_owned()), - }); + // the same identifiables can be present in multiple files + // in this case we only keep the first one + if let Some(existing_element) = data.identifiables.get(&key).and_then(|weak_el| weak_el.upgrade()) { + // present in both + if let Some(new_element) = value.upgrade() { + if existing_element.element_name() != new_element.element_name() { + // referenced element is different on both sides + return Err(AutosarDataError::OverlappingDataError { + filename, + path: new_element.xml_path(), + }); + } } + } else { + data.identifiables.insert(key, value); } } data.reference_origins.reserve(parser.references.len()); @@ -156,6 +208,269 @@ impl AutosarProject { Ok((arxml_file, parser.warnings)) } + // Merge the elements from an incoming arxml file into the overall model + // + // The Autosar standard specifies that the data can be split across multiple arxml files + // It states that each ARXML file can represent an "AUTOSAR Partial Model". + // The possible partitioning is marked in the meta model, where some elements have the attribute "splitable". + // These are the points where the overall elements can be split into different arxml files, or, while loading, merged. + // Unfortunately, the standard says nothing about how this should be done, so the algorithm here is just a guess. + // In the wild, only merging at the AR-PACKAGES and at the ELEMENTS level exists. Everything else seems like a bad idea anyway. + fn merge_file_data(&self, new_root: &Element, new_file: WeakArxmlFile) -> Result<(), AutosarDataError> { + let root = self.root_element(); + let files: HashSet = self.files().map(|f| f.downgrade()).collect(); + + AutosarProject::merge_element(&root, &files, new_root, new_file)?; + + Ok(()) + } + + fn merge_element( + parent_a: &Element, + files: &HashSet, + parent_b: &Element, + new_file: WeakArxmlFile, + ) -> Result<(), AutosarDataError> { + let mut iter_a = parent_a.sub_elements().enumerate(); + let mut iter_b = parent_b.sub_elements(); + let mut item_a = iter_a.next(); + let mut item_b = iter_b.next(); + let mut elements_a_only = Vec::::new(); + let mut elements_b_only = Vec::<(Element, usize)>::new(); + let mut elements_merge = Vec::<(Element, Element)>::new(); + let min_ver_a = files + .iter() + .filter_map(|weak| weak.upgrade().map(|f| f.version())) + .min() + .unwrap_or(AutosarVersion::LATEST); + let min_ver_b = new_file + .upgrade() + .map(|f| f.version()) + .unwrap_or(AutosarVersion::LATEST); + let version = std::cmp::min(min_ver_a, min_ver_b); + let splitable = parent_a.element_type().splittable_in(version); + + while let (Some((pos_a, elem_a)), Some(elem_b)) = (&item_a, &item_b) { + if elem_a.element_name() == elem_b.element_name() { + if elem_a.is_identifiable() { + if elem_a.item_name() == elem_b.item_name() { + // equal + // advance both iterators + elements_merge.push((elem_a.clone(), elem_b.clone())); + item_a = iter_a.next(); + item_b = iter_b.next(); + } else { + // assume that the ordering on both sides is different + // find a match for a among the siblings of b + if let Some(sibling) = parent_b + .sub_elements() + .find(|e| e.element_name() == elem_a.element_name() && e.item_name() == elem_a.item_name()) + { + // matching item found + elements_merge.push((elem_a.clone(), sibling.clone())); + } else { + // element is unique in a + if splitable { + elements_a_only.push(elem_a.clone()); + } else { + return Err(AutosarDataError::InvalidFileMerge { + path: parent_a.xml_path(), + }); + } + } + item_a = iter_a.next(); + } + } else { + // special case for BSW parameters - many elements used here don't have a SHORT-NAME, but they do have a DEFINITION-REF + let defref_a = elem_a + .get_sub_element(ElementName::DefinitionRef) + .and_then(|dr| dr.character_data()) + .and_then(|cdata| cdata.string_value()); + let defref_b = elem_b + .get_sub_element(ElementName::DefinitionRef) + .and_then(|dr| dr.character_data()) + .and_then(|cdata| cdata.string_value()); + // defref_a and _b are simply none for all other elements which don't have a definition-ref + if defref_a == defref_b { + // either: defrefs exist and are identical, OR they are both None + // if they are None, then there is nothing else that can be compared, so we just assume the elements are identical + // advance both iterators + elements_merge.push((elem_a.clone(), elem_b.clone())); + item_a = iter_a.next(); + item_b = iter_b.next(); + } else { + // check if a sibling of elem_b has the same definiton-ref as elem_a + // this handles the case where the the elements on both sides are ordered differently + if let Some(sibling) = parent_b + .sub_elements() + .filter(|e| e.element_name() == elem_a.element_name()) + .find(|e| { + e.get_sub_element(ElementName::DefinitionRef) + .and_then(|dr| dr.character_data()) + .and_then(|cdata| cdata.string_value()) + == defref_a + }) + { + // a match for item_a exists + elements_merge.push((elem_a.clone(), sibling.clone())); + } else { + // element is unique in a + if splitable { + elements_a_only.push(elem_a.clone()); + } else { + return Err(AutosarDataError::InvalidFileMerge { + path: parent_a.xml_path(), + }); + } + } + item_a = iter_a.next(); + } + } + } else { + // a and b are different kinds of elements. This is only allowed if parent is splittable + let parent_type = parent_a.element_type(); + // The following check does not work, real examples still fail: + // if !parent_type.splittable_in(self.version()) && parent_a.element_name() != ElementName::ArPackage { + // return Err(AutosarDataError::InvalidFileMerge { path: parent_a.xml_path() }); + // } + + let (_, indices_a) = parent_type.find_sub_element(elem_a.element_name(), u32::MAX).unwrap(); + let (_, indices_b) = parent_type.find_sub_element(elem_b.element_name(), u32::MAX).unwrap(); + if indices_a < indices_b { + // elem_a comes before elem_b, advance only a + // a: | + // b: | + elements_a_only.push(elem_a.clone()); + item_a = iter_a.next(); + } else { + // elem_b comes before elem_a, advance only b + // a: | + // b: | + if !elements_merge.iter().any(|(_, merge_b)| merge_b == elem_b) { + elements_b_only.push((elem_b.clone(), *pos_a)); + item_b = iter_b.next(); + } + } + } + } + // at least one of the two iterators has reached the end + // make sure the other one also reaches the end + if let Some((_, elem_a)) = item_a { + elements_a_only.push(elem_a); + for (_, elem_a) in iter_a { + elements_a_only.push(elem_a); + } + } + if let Some(elem_b) = item_b { + let elem_count = parent_a.0.lock().content.len(); + if !elements_merge.iter().any(|(_, merge_b)| merge_b == &elem_b) { + elements_b_only.push((elem_b, elem_count)); + } + for elem_b in iter_b { + if !elements_merge.iter().any(|(_, merge_b)| merge_b == &elem_b) { + elements_b_only.push((elem_b, elem_count)); + } + } + } + + // elements in elements_a_only are already present in the model, so they only need to be restricted + for element in elements_a_only { + // files contains the permisions of the parent + if element.0.lock().file_membership.is_empty() { + element.0.lock().file_membership = files.to_owned() + } + } + // elements in elements_b_only are not present in the model. They need to be moved over and inserted at a reasonable position + let mut parent_a_locked = parent_a.0.lock(); + for (idx, (new_element, insert_pos)) in elements_b_only.into_iter().enumerate() { + new_element.set_parent(ElementOrProject::Element(parent_a.downgrade())); + // restrict new_element, it is only present in new_file + new_element.0.lock().file_membership.insert(new_file.clone()); + // add the new_element (from side b) to the content of parent_a + // to do this, first check valid element insertion positions + let (first_pos, last_pos) = parent_a_locked + .find_element_insert_pos(new_element.element_name(), version) + .map_err(|_| AutosarDataError::InvalidFileMerge { + path: new_element.element_name().to_string(), + })?; + // idx number of elements have already been inserted, so the destination position must be adjusted + let dest = insert_pos + idx; + if dest < first_pos { + // desired position is before the first allowed position + parent_a_locked + .content + .insert(first_pos, ElementContent::Element(new_element)); + } else if first_pos <= dest && dest <= last_pos { + parent_a_locked + .content + .insert(dest, ElementContent::Element(new_element)); + } else { + // desired position is after the last allowed position + parent_a_locked + .content + .insert(last_pos, ElementContent::Element(new_element)); + } + } + drop(parent_a_locked); + + // recurse for elements that need to be merged + for (elem_a, elem_b) in elements_merge { + let files = elem_a.0.lock().file_membership.clone(); + AutosarProject::merge_element(&elem_a, &files, &elem_b, new_file.clone())?; + if !elem_a.0.lock().file_membership.is_empty() { + elem_a.0.lock().file_membership.insert(new_file.clone()); + } + } + + Ok(()) + } + + /// clean up the model after a file is removed or after a failed file merge during load + fn unmerge_file(&self, file: &WeakArxmlFile) { + let mut elements_to_delete = vec![]; + for (_, e) in self.elements_dfs() { + let mut fm = e.file_membership_local(); + if fm.contains(file) { + if fm.len() == 1 { + // this element is only part of the file that is being removed + // queue the deletion of this element (the iterator might skip items if the model is mutated while it is active) + elements_to_delete.push(e); + } else { + // the element is part of several files, so this one only needs to be removed from the set + fm.remove(file); + e.set_file_membership(fm); + } + } + } + + // perform any queued deletions + for e in elements_to_delete { + if let Ok(Some(parent)) = e.parent() { + let _ = parent.remove_sub_element(e); + } + } + + let mut self_locked = self.0.lock(); + + // clean up any remaining references to now-deleted identifiables + let mut paths_to_remove = vec![]; + for (path, weak_elem) in &self_locked.identifiables { + if weak_elem.upgrade().is_none() { + paths_to_remove.push(path.to_owned()); + } + } + for path in paths_to_remove { + self_locked.identifiables.remove(&path); + } + + let mut ref_origins_old = FxHashMap::>::default(); + std::mem::swap(&mut ref_origins_old, &mut self_locked.reference_origins); + for (refpath, ref_elements) in &ref_origins_old { + let newvec: Vec = ref_elements.iter().filter(|e| e.upgrade().is_some()).cloned().collect(); + self_locked.reference_origins.insert(refpath.to_owned(), newvec); + } + } + /// Load an arxml file /// /// This function is a wrapper around load_named_arxml_buffer to make the common case of loading a file from disk more convenient @@ -240,11 +555,15 @@ impl AutosarProject { // find_result is stored first so that the lock on project is dropped if let Some(pos) = find_result { self.0.lock().files.swap_remove(pos); - let root_elem = file.root_element(); - root_elem - .0 - .lock() - .remove_internal(root_elem.downgrade(), self, Cow::from("")); + if self.0.lock().files.is_empty() { + // no other files remain in the project, so it reverts to being empty + self.root_element().0.lock().content.clear(); + self.0.lock().identifiables.clear(); + self.0.lock().reference_origins.clear(); + } else { + // other files still contribute elements, so only the elements specifically associated with this file should be removed + self.unmerge_file(&file.downgrade()); + } } } @@ -268,8 +587,9 @@ impl AutosarProject { pub fn serialize_files(&self) -> HashMap { let mut result = HashMap::new(); for file in self.files() { - let data = file.serialize(); - result.insert(file.filename(), data); + if let Ok(data) = file.serialize() { + result.insert(file.filename(), data); + } } result } @@ -326,6 +646,21 @@ impl AutosarProject { ArxmlFileIterator::new(self.clone()) } + /// Get a referenct to the root `````` element of this project + /// + /// # Example + /// + /// ``` + /// # use autosar_data::*; + /// # let project = AutosarProject::new(); + /// # let _file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap(); + /// let autosar_element = project.root_element(); + /// ``` + pub fn root_element(&self) -> Element { + let locked_proj = self.0.lock(); + locked_proj.root_element.clone() + } + /// get a named element by its Autosar path /// /// This is a lookup in a hash table and runs in O(1) time @@ -352,9 +687,11 @@ impl AutosarProject { project.identifiables.get(path).and_then(|element| element.upgrade()) } - /// create a depth-first iterator over all [Element]s in all [ArxmlFile]s + /// create a depth-first iterator over all [Element]s in the project + /// + /// The iterator returns all elements from the merged model, consisting of + /// data from all arxml files loaded in this project. /// - /// The AUTOSAR elements in each file will appear at depth = 0. /// Directly printing the return values could show something like this: /// ///
@@ -377,8 +714,28 @@ impl AutosarProject {
     /// # Ok(())
     /// # }
     /// ```
-    pub fn elements_dfs(&self) -> AutosarDataElementsDfsIterator {
-        AutosarDataElementsDfsIterator::new(self.files())
+    pub fn elements_dfs(&self) -> ElementsDfsIterator {
+        self.root_element().elements_dfs()
+    }
+
+    /// Recursively sort all elements in the project. This is exactly identical to calling sort() on the root element of the project.
+    ///
+    /// All sub elements of the root element are sorted alphabetically.
+    /// If the sub-elements are named, then the sorting is performed according to the item names,
+    /// otherwise the serialized form of the sub-elements is used for sorting.
+    ///
+    /// Element attributes are not taken into account while sorting.
+    /// The elements are sorted in place, and sorting cannot fail, so there is no return value.
+    ///
+    /// # Example
+    /// ```
+    /// # use autosar_data::*;
+    /// # let project = AutosarProject::new();
+    /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
+    /// project.sort();
+    /// ```
+    pub fn sort(&self) {
+        self.root_element().sort()
     }
 
     /// create an iterator over all identifiable elements
@@ -525,8 +882,9 @@ impl AutosarProject {
                 if suffix.is_empty() || suffix.starts_with('/') {
                     let new_key = format!("{new_path}{suffix}");
                     // fix the identifiables hashmap
-                    let entry = project.identifiables.remove(&key).unwrap();
-                    project.identifiables.insert(new_key, entry);
+                    if let Some(entry) = project.identifiables.remove(&key) {
+                        project.identifiables.insert(new_key, entry);
+                    }
                 }
             }
         }
@@ -586,6 +944,17 @@ impl AutosarProject {
     }
 }
 
+impl AutosarProjectRaw {
+    pub(crate) fn set_version(&mut self, new_ver: AutosarVersion) {
+        let attribute_value = CharacterData::String(format!("http://autosar.org/schema/r4.0 {}", new_ver.filename()));
+        let _ = self.root_element.0.lock().set_attribute_internal(
+            AttributeName::xsiSchemalocation,
+            attribute_value,
+            new_ver,
+        );
+    }
+}
+
 impl Default for AutosarProject {
     fn default() -> Self {
         Self::new()
@@ -623,13 +992,29 @@ mod test {
         const FILEBUF: &str = r#"
         
         
-          Pkg
+          
+            Pkg
+            
+              Thing
+            
+          
         "#;
         const FILEBUF2: &str = r#"
         
         
           OtherPkg
         "#;
+        const FILEBUF3: &str = r#"
+        
+        
+          
+            Pkg
+            
+            Thing
+            
+          
+        "#;
+        const NON_ARXML: &str = "The quick brown fox jumps over the lazy dog";
         let project = AutosarProject::new();
         // succefully load a buffer
         let result = project.load_named_arxml_buffer(FILEBUF.as_bytes(), "test", true);
@@ -641,7 +1026,10 @@ mod test {
         let result = project.load_named_arxml_buffer(FILEBUF.as_bytes(), "test", true);
         assert!(result.is_err());
         // error: overlapping autosar paths
-        let result = project.load_named_arxml_buffer(FILEBUF.as_bytes(), "test2", true);
+        let result = project.load_named_arxml_buffer(FILEBUF3.as_bytes(), "test2", true);
+        assert!(result.is_err());
+        // error: not arxml data
+        let result = project.load_named_arxml_buffer(NON_ARXML.as_bytes(), "nonsense", true);
         assert!(result.is_err());
     }
 
@@ -651,6 +1039,93 @@ mod test {
         assert!(project.load_arxml_file("nonexistent", true).is_err());
     }
 
+    #[test]
+    fn data_merge() {
+        const FILEBUF1: &[u8] = r#"
+        
+        
+          Pkg_A
+            BswModule
+              BswModuleValues
+              
+                
+                  /REF_A
+                
+                
+                  /REF_B
+                
+              
+            
+          
+          Pkg_B
+        "#.as_bytes();
+        const FILEBUF2: &[u8] = r#"
+        
+        
+          Pkg_B
+          Pkg_A
+            BswModule
+              BswModuleValues
+              
+                
+                  /REF_B
+                
+                
+                  /REF_A
+                
+              
+            
+          
+        "#.as_bytes();
+        // test with re-ordered identifiable elements and re-ordered BSW parameter values
+        // both should be recognized and merged, so that the total number of elements does not increase
+        let project = AutosarProject::new();
+        let result = project.load_named_arxml_buffer(FILEBUF1, "test1", true);
+        assert!(result.is_ok());
+        let elemcount = project.elements_dfs().count();
+        let result = project.load_named_arxml_buffer(FILEBUF2, "test2", true);
+        assert!(result.is_ok());
+        let elemcount2 = project.elements_dfs().count();
+        // the second file is identical to the first, except for ordering.
+        // The total number of elements should not grow as a result of loading it.
+        assert_eq!(elemcount, elemcount2);
+
+        // the following two files diverge on the TIMING-RESOURCE element
+        // this is not permitted, because SYSTEM-TIMING is not splittable
+        const ERRFILE1: &[u8] = r#"
+        
+        Package
+          
+            
+              SystemTimings
+              CAT
+              
+                Name_One
+              
+            
+          
+        "#.as_bytes();
+        const ERRFILE2: &[u8] = r#"
+        
+        Package
+          
+            
+              SystemTimings
+              
+                Name_Two
+              
+            
+          
+        "#.as_bytes();
+        let project = AutosarProject::new();
+        let result = project.load_named_arxml_buffer(ERRFILE1, "test1", true);
+        println!("{result:#?}");
+        assert!(result.is_ok());
+        let result = project.load_named_arxml_buffer(ERRFILE2, "test2", true);
+        let error = result.unwrap_err();
+        assert!(matches!(error, AutosarDataError::InvalidFileMerge { .. }));
+    }
+
     #[test]
     fn remove_file() {
         const FILEBUF: &str = r#"
@@ -759,7 +1234,34 @@ mod test {
 
         let result = project.serialize_files();
         assert_eq!(result.len(), 2);
-        assert_eq!(result.get(&PathBuf::from("filename1")).unwrap(), &file1.serialize());
-        assert_eq!(result.get(&PathBuf::from("filename2")).unwrap(), &file2.serialize());
+        assert_eq!(
+            result.get(&PathBuf::from("filename1")).unwrap(),
+            &file1.serialize().unwrap()
+        );
+        assert_eq!(
+            result.get(&PathBuf::from("filename2")).unwrap(),
+            &file2.serialize().unwrap()
+        );
+    }
+
+    #[test]
+    fn traits() {
+        // AutosarProject: Debug, Clone
+        let project = AutosarProject::new();
+        let p2 = project.clone();
+        assert_eq!(project, p2);
+        assert_eq!(format!("{project:#?}"), format!("{p2:#?}"));
+
+        // CharacterData
+        let cdata = CharacterData::String("x".to_string());
+        let cdata2 = cdata.clone();
+        assert_eq!(cdata, cdata2);
+        assert_eq!(format!("{cdata:#?}"), format!("{cdata2:#?}"));
+
+        // ContentType 
+        let ct: ContentType = ContentType::Elements;
+        let ct2 = ct.clone();
+        assert_eq!(ct, ct2);
+        assert_eq!(format!("{ct:#?}"), format!("{ct2:#?}"));
     }
 }
diff --git a/autosar-data/src/chardata.rs b/autosar-data/src/chardata.rs
index 8d6043c..87884ca 100644
--- a/autosar-data/src/chardata.rs
+++ b/autosar-data/src/chardata.rs
@@ -24,16 +24,14 @@ impl CharacterData {
                 check_fn, max_length, ..
             } => {
                 if let CharacterData::String(stringval) = &value {
-                    if (max_length.is_none() || stringval.len() <= max_length.unwrap())
-                        && check_fn(stringval.as_bytes())
-                    {
+                    if stringval.len() <= max_length.unwrap_or(usize::MAX) && check_fn(stringval.as_bytes()) {
                         return true;
                     }
                 }
             }
             CharacterDataSpec::String { max_length, .. } => {
                 if let CharacterData::String(stringval) = &value {
-                    if max_length.is_none() || stringval.len() <= max_length.unwrap() {
+                    if stringval.len() <= max_length.unwrap_or(usize::MAX) {
                         return true;
                     }
                 }
@@ -91,12 +89,12 @@ impl CharacterData {
             CharacterDataSpec::Pattern {
                 check_fn, max_length, ..
             } => {
-                if (max_length.is_none() || input.len() <= max_length.unwrap()) && check_fn(input.as_bytes()) {
+                if input.len() <= max_length.unwrap_or(usize::MAX) && check_fn(input.as_bytes()) {
                     return Some(CharacterData::String(input.to_owned()));
                 }
             }
             CharacterDataSpec::String { max_length, .. } => {
-                if max_length.is_none() || input.len() <= max_length.unwrap() {
+                if input.len() <= max_length.unwrap_or(usize::MAX) {
                     return Some(CharacterData::String(input.to_owned()));
                 }
             }
@@ -214,69 +212,80 @@ mod test {
             items: &[(EnumItem::default, 0x3ffff), (EnumItem::preserve, 0x0ffff)],
         };
         let data = CharacterData::Enum(EnumItem::default);
-        assert_eq!(
-            CharacterData::check_value(&data, &spec_enum, AutosarVersion::Autosar_00050),
-            true
-        );
+        assert!(CharacterData::check_value(
+            &data,
+            &spec_enum,
+            AutosarVersion::Autosar_00050
+        ));
         let data = CharacterData::Enum(EnumItem::preserve);
-        assert_eq!(
-            CharacterData::check_value(&data, &spec_enum, AutosarVersion::Autosar_00050),
-            false
-        );
+        assert!(!CharacterData::check_value(
+            &data,
+            &spec_enum,
+            AutosarVersion::Autosar_00050
+        ));
         let data = CharacterData::Enum(EnumItem::Abstract);
-        assert_eq!(
-            CharacterData::check_value(&data, &spec_enum, AutosarVersion::Autosar_00050),
-            false
-        );
+        assert!(!CharacterData::check_value(
+            &data,
+            &spec_enum,
+            AutosarVersion::Autosar_00050
+        ));
         let data = CharacterData::Double(1.23);
-        assert_eq!(
-            CharacterData::check_value(&data, &spec_enum, AutosarVersion::Autosar_00050),
-            false
-        );
+        assert!(!CharacterData::check_value(
+            &data,
+            &spec_enum,
+            AutosarVersion::Autosar_00050
+        ));
 
         let spec_double = CharacterDataSpec::Double;
         let data = CharacterData::Double(1.23);
-        assert_eq!(
-            CharacterData::check_value(&data, &spec_double, AutosarVersion::Autosar_00050),
-            true
-        );
+        assert!(CharacterData::check_value(
+            &data,
+            &spec_double,
+            AutosarVersion::Autosar_00050
+        ));
         let data = CharacterData::String("1.23".to_string());
-        assert_eq!(
-            CharacterData::check_value(&data, &spec_double, AutosarVersion::Autosar_00050),
-            false
-        );
+        assert!(!CharacterData::check_value(
+            &data,
+            &spec_double,
+            AutosarVersion::Autosar_00050
+        ));
 
         let spec_uint = CharacterDataSpec::UnsignedInteger;
         let data = CharacterData::UnsignedInteger(123);
-        assert_eq!(
-            CharacterData::check_value(&data, &spec_uint, AutosarVersion::Autosar_00050),
-            true
-        );
+        assert!(CharacterData::check_value(
+            &data,
+            &spec_uint,
+            AutosarVersion::Autosar_00050
+        ));
         let data = CharacterData::String("123".to_string());
-        assert_eq!(
-            CharacterData::check_value(&data, &spec_uint, AutosarVersion::Autosar_00050),
-            false
-        );
+        assert!(!CharacterData::check_value(
+            &data,
+            &spec_uint,
+            AutosarVersion::Autosar_00050
+        ));
 
         let spec_string = CharacterDataSpec::String {
             preserve_whitespace: false,
             max_length: Some(10),
         };
         let data = CharacterData::String("123".to_string());
-        assert_eq!(
-            CharacterData::check_value(&data, &spec_string, AutosarVersion::Autosar_00050),
-            true
-        );
+        assert!(CharacterData::check_value(
+            &data,
+            &spec_string,
+            AutosarVersion::Autosar_00050
+        ));
         let data = CharacterData::String("12345678901".to_string());
-        assert_eq!(
-            CharacterData::check_value(&data, &spec_string, AutosarVersion::Autosar_00050),
-            false
-        );
+        assert!(!CharacterData::check_value(
+            &data,
+            &spec_string,
+            AutosarVersion::Autosar_00050
+        ));
         let data = CharacterData::UnsignedInteger(1);
-        assert_eq!(
-            CharacterData::check_value(&data, &spec_string, AutosarVersion::Autosar_00050),
-            false
-        );
+        assert!(!CharacterData::check_value(
+            &data,
+            &spec_string,
+            AutosarVersion::Autosar_00050
+        ));
 
         let spec_pattern = CharacterDataSpec::Pattern {
             check_fn: dummy_validate,
@@ -284,15 +293,17 @@ mod test {
             max_length: Some(1),
         };
         let data = CharacterData::String("0".to_string());
-        assert_eq!(
-            CharacterData::check_value(&data, &spec_pattern, AutosarVersion::Autosar_00050),
-            true
-        );
+        assert!(CharacterData::check_value(
+            &data,
+            &spec_pattern,
+            AutosarVersion::Autosar_00050
+        ));
         let data = CharacterData::String("2".to_string());
-        assert_eq!(
-            CharacterData::check_value(&data, &spec_pattern, AutosarVersion::Autosar_00050),
-            false
-        );
+        assert!(!CharacterData::check_value(
+            &data,
+            &spec_pattern,
+            AutosarVersion::Autosar_00050
+        ));
     }
 
     #[test]
@@ -302,15 +313,15 @@ mod test {
         };
         let data = CharacterData::Enum(EnumItem::default);
         let (result, _) = data.check_version_compatibility(&spec_enum, AutosarVersion::Autosar_4_0_1);
-        assert_eq!(result, true);
+        assert!(result);
         let (result, _) = data.check_version_compatibility(&spec_enum, AutosarVersion::Autosar_00050);
-        assert_eq!(result, false);
+        assert!(!result);
         let data = CharacterData::Enum(EnumItem::Abstract);
         let (result, _) = data.check_version_compatibility(&spec_enum, AutosarVersion::Autosar_00050);
-        assert_eq!(result, false);
+        assert!(!result);
         let data = CharacterData::UnsignedInteger(0);
         let (result, _) = data.check_version_compatibility(&spec_enum, AutosarVersion::Autosar_00050);
-        assert_eq!(result, false);
+        assert!(!result);
     }
 
     #[test]
diff --git a/autosar-data/src/element.rs b/autosar-data/src/element.rs
index a6992bc..f141567 100644
--- a/autosar-data/src/element.rs
+++ b/autosar-data/src/element.rs
@@ -17,7 +17,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
+    /// # let element = project.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
     /// if let Some(parent) = element.parent()? {
     ///     // ...
     /// }
@@ -32,7 +32,7 @@ impl Element {
         self.0.lock().parent()
     }
 
-    pub(crate) fn set_parent(&self, new_parent: ElementOrFile) {
+    pub(crate) fn set_parent(&self, new_parent: ElementOrProject) {
         self.0.lock().set_parent(new_parent)
     }
 
@@ -44,7 +44,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// let element = file.root_element();
+    /// let element = project.root_element();
     /// let element_name = element.element_name();
     /// assert_eq!(element_name, ElementName::Autosar);
     /// ```
@@ -62,7 +62,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element();
+    /// # let element = project.root_element();
     /// let element_type = element.element_type();
     /// ```
     pub fn element_type(&self) -> ElementType {
@@ -81,7 +81,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
+    /// # let element = project.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
     /// if let Some(item_name) = element.item_name() {
     ///     // ...
     /// }
@@ -100,7 +100,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
+    /// # let element = project.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
     /// element.set_item_name("NewName");
     /// ```
     ///
@@ -111,7 +111,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
+    /// # let element = project.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
     /// if let Some(short_name) = element.get_sub_element(ElementName::ShortName) {
     ///     short_name.set_character_data(CharacterData::String("the_new_name".to_string()));
     /// }
@@ -129,9 +129,8 @@ impl Element {
         if new_name.is_empty() {
             return Err(AutosarDataError::ItemNameRequired);
         }
-        let file = self.file()?;
-        let project = file.project()?;
-        let version = file.version();
+        let project = self.project()?;
+        let version = self.min_version()?;
         self.0.lock().set_item_name(new_name, &project, version)
     }
 
@@ -145,7 +144,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element();
+    /// # let element = project.root_element();
     /// if element.is_identifiable() {
     ///     // ...
     /// }
@@ -164,7 +163,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
+    /// # let element = project.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
     /// if element.is_reference() {
     ///     // ex: element.set_reference_target(...)
     /// }
@@ -182,7 +181,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
+    /// # let element = project.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
     /// let path = element.path()?;
     /// # Ok(())
     /// # }
@@ -199,7 +198,7 @@ impl Element {
         self.0.lock().path()
     }
 
-    /// Get a reference to the [ArxmlFile] containing the current element
+    /// Get a reference to the [AutosarProject] containing the current element
     ///
     /// # Example
     ///
@@ -208,8 +207,8 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
-    /// let file = element.file()?;
+    /// # let element = project.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
+    /// let file = element.project()?;
     /// # Ok(())
     /// # }
     /// ```
@@ -220,7 +219,7 @@ impl Element {
     ///  - [AutosarDataError::ParentElementLocked]: a parent element was locked and did not become available after waiting briefly.
     ///    The operation was aborted to avoid a deadlock, but can be retried.
     ///
-    pub fn file(&self) -> Result {
+    pub fn project(&self) -> Result {
         let mut cur_elem = self.clone();
         loop {
             let parent = {
@@ -229,13 +228,13 @@ impl Element {
                     .try_lock_for(std::time::Duration::from_millis(10))
                     .ok_or(AutosarDataError::ParentElementLocked)?;
                 match &element.parent {
-                    ElementOrFile::Element(weak_parent) => {
+                    ElementOrProject::Element(weak_parent) => {
                         weak_parent.upgrade().ok_or(AutosarDataError::ItemDeleted)?
                     }
-                    ElementOrFile::File(weak_arxmlfile) => {
+                    ElementOrProject::Project(weak_arxmlfile) => {
                         return weak_arxmlfile.upgrade().ok_or(AutosarDataError::ItemDeleted)
                     }
-                    ElementOrFile::None => return Err(AutosarDataError::ItemDeleted),
+                    ElementOrProject::None => return Err(AutosarDataError::ItemDeleted),
                 }
             };
             cur_elem = parent;
@@ -250,7 +249,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element();
+    /// # let element = project.root_element();
     /// if element.content_type() == ContentType::CharacterData {
     ///     // ...
     /// }
@@ -277,7 +276,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// let element = file.root_element().create_sub_element(ElementName::ArPackages)?;
+    /// let element = project.root_element().create_sub_element(ElementName::ArPackages)?;
     /// # Ok(())
     /// # }
     /// ```
@@ -292,9 +291,10 @@ impl Element {
     ///  - [AutosarDataError::InvalidSubElement]: The ElementName is not a valid sub element according to the specification.
     ///  - [AutosarDataError::ItemNameRequired]: The sub element requires an item name, so you must use create_named_sub_element().
     pub fn create_sub_element(&self, element_name: ElementName) -> Result {
-        let version = self.file().map(|f| f.version())?;
+        let version = self.min_version()?;
         self.0
-            .lock()
+            .try_lock()
+            .ok_or(AutosarDataError::ParentElementLocked)?
             .create_sub_element(self.downgrade(), element_name, version)
     }
 
@@ -312,7 +312,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// let element = file.root_element().create_sub_element_at(ElementName::ArPackages, 0)?;
+    /// let element = project.root_element().create_sub_element_at(ElementName::ArPackages, 0)?;
     /// # Ok(())
     /// # }
     /// ```
@@ -332,7 +332,7 @@ impl Element {
         element_name: ElementName,
         position: usize,
     ) -> Result {
-        let version = self.file().map(|f| f.version())?;
+        let version = self.min_version()?;
         self.0
             .lock()
             .create_sub_element_at(self.downgrade(), element_name, position, version)
@@ -351,7 +351,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// let pkgs_element = file.root_element().create_sub_element(ElementName::ArPackages)?;
+    /// let pkgs_element = project.root_element().create_sub_element(ElementName::ArPackages)?;
     /// let element = pkgs_element.create_named_sub_element(ElementName::ArPackage, "Package")?;
     /// # Ok(())
     /// # }
@@ -371,9 +371,8 @@ impl Element {
         element_name: ElementName,
         item_name: &str,
     ) -> Result {
-        let file = self.file()?;
-        let project = file.project()?;
-        let version = file.version();
+        let project = self.project()?;
+        let version = self.min_version()?;
         self.0
             .lock()
             .create_named_sub_element(self.downgrade(), element_name, item_name, &project, version)
@@ -393,7 +392,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// let pkgs_element = file.root_element().create_sub_element(ElementName::ArPackages)?;
+    /// let pkgs_element = project.root_element().create_sub_element(ElementName::ArPackages)?;
     /// let element = pkgs_element.create_named_sub_element_at(ElementName::ArPackage, "Package", 0)?;
     /// # Ok(())
     /// # }
@@ -415,9 +414,8 @@ impl Element {
         item_name: &str,
         position: usize,
     ) -> Result {
-        let file = self.file()?;
-        let project = file.project()?;
-        let version = file.version();
+        let project = self.project()?;
+        let version = self.min_version()?;
         self.0.lock().create_named_sub_element_at(
             self.downgrade(),
             element_name,
@@ -448,7 +446,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let pkgs_element = file.root_element().create_sub_element(ElementName::ArPackages)?;
+    /// # let pkgs_element = project.root_element().create_sub_element(ElementName::ArPackages)?;
     /// # let base = pkgs_element.create_named_sub_element(ElementName::ArPackage, "Package")
     /// #    .and_then(|p| p.create_sub_element(ElementName::Elements))?;
     /// # base.create_named_sub_element(ElementName::System, "Path")?;
@@ -467,9 +465,8 @@ impl Element {
     ///  - [AutosarDataError::ElementInsertionConflict]: The requested sub element cannot be created because it conflicts with an existing sub element.
     ///  - [AutosarDataError::InvalidSubElement]: The ElementName is not a valid sub element according to the specification.
     pub fn create_copied_sub_element(&self, other: &Element) -> Result {
-        let file = self.file()?;
-        let project = file.project()?;
-        let version = file.version();
+        let project = self.project()?;
+        let version = self.min_version()?;
         self.0
             .lock()
             .create_copied_sub_element(self.downgrade(), other, &project, version)
@@ -495,7 +492,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let pkgs_element = file.root_element().create_sub_element(ElementName::ArPackages)?;
+    /// # let pkgs_element = project.root_element().create_sub_element(ElementName::ArPackages)?;
     /// # let base = pkgs_element.create_named_sub_element(ElementName::ArPackage, "Package")
     /// #    .and_then(|p| p.create_sub_element(ElementName::Elements))?;
     /// # base.create_named_sub_element(ElementName::System, "Path")?;
@@ -515,9 +512,8 @@ impl Element {
     ///  - [AutosarDataError::InvalidSubElement]: The ElementName is not a valid sub element according to the specification.
     ///  - [AutosarDataError::InvalidPosition]: This sub element cannot be created at the requested position.
     pub fn create_copied_sub_element_at(&self, other: &Element, position: usize) -> Result {
-        let file = self.file()?;
-        let project = file.project()?;
-        let version = file.version();
+        let project = self.project()?;
+        let version = self.min_version()?;
         self.0
             .lock()
             .create_copied_sub_element_at(self.downgrade(), other, position, &project, version)
@@ -538,7 +534,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let pkgs_element = file.root_element().create_sub_element(ElementName::ArPackages)?;
+    /// # let pkgs_element = project.root_element().create_sub_element(ElementName::ArPackages)?;
     /// # let base = pkgs_element.create_named_sub_element(ElementName::ArPackage, "Package")
     /// #    .and_then(|p| p.create_sub_element(ElementName::Elements))?;
     /// # base.create_named_sub_element(ElementName::System, "Path")?;
@@ -556,16 +552,18 @@ impl Element {
     ///  - [AutosarDataError::IncorrectContentType]: A sub element may not be created in an element with content type CharacterData.
     ///  - [AutosarDataError::ElementInsertionConflict]: The requested sub element cannot be created because it conflicts with an existing sub element.
     ///  - [AutosarDataError::InvalidSubElement]: The ElementName is not a valid sub element according to the specification.
-    ///  - [AutosarDataError::VersionIncompatible]: The Autosar versions of the source and destination are different
+    ///  - [AutosarDataError::VersionMismatch]: The Autosar versions of the source and destination are different
     ///  - [AutosarDataError::ForbiddenMoveToSubElement]: The destination is a sub element of the source. Moving here is not possible
     pub fn move_element_here(&self, move_element: &Element) -> Result {
-        let file_src = move_element.file()?;
-        let project_src = file_src.project()?;
-        let version = self.file().map(|f| f.version())?;
-        let project = self.file().and_then(|f| f.project())?;
-        let version_src = file_src.version();
+        let project_src = move_element.project()?;
+        let project = self.project()?;
+        let version_src = move_element.min_version()?;
+        let version = self.min_version()?;
         if version != version_src {
-            return Err(AutosarDataError::VersionIncompatible);
+            return Err(AutosarDataError::VersionMismatch {
+                version_cur: version,
+                version_new: version_src,
+            });
         }
         self.0
             .lock()
@@ -587,7 +585,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let pkgs_element = file.root_element().create_sub_element(ElementName::ArPackages)?;
+    /// # let pkgs_element = project.root_element().create_sub_element(ElementName::ArPackages)?;
     /// # let base = pkgs_element.create_named_sub_element(ElementName::ArPackage, "Package")
     /// #    .and_then(|p| p.create_sub_element(ElementName::Elements))?;
     /// # base.create_named_sub_element(ElementName::System, "Path")?;
@@ -605,17 +603,19 @@ impl Element {
     ///  - [AutosarDataError::IncorrectContentType]: A sub element may not be created in an element with content type CharacterData.
     ///  - [AutosarDataError::ElementInsertionConflict]: The requested sub element cannot be created because it conflicts with an existing sub element.
     ///  - [AutosarDataError::InvalidSubElement]: The ElementName is not a valid sub element according to the specification.
-    ///  - [AutosarDataError::VersionIncompatible]: The Autosar versions of the source and destination are different
+    ///  - [AutosarDataError::VersionMismatch]: The Autosar versions of the source and destination are different
     ///  - [AutosarDataError::ForbiddenMoveToSubElement]: The destination is a sub element of the source. Moving here is not possible
     ///  - [AutosarDataError::InvalidPosition]: This sub element cannot be created at the requested position.
     pub fn move_element_here_at(&self, move_element: &Element, position: usize) -> Result {
-        let file_src = move_element.file()?;
-        let project_src = file_src.project()?;
-        let version = self.file().map(|f| f.version())?;
-        let project = self.file().and_then(|f| f.project())?;
-        let version_src = file_src.version();
+        let project_src = move_element.project()?;
+        let project = self.project()?;
+        let version_src = move_element.min_version()?;
+        let version = self.min_version()?;
         if version != version_src {
-            return Err(AutosarDataError::VersionIncompatible);
+            return Err(AutosarDataError::VersionMismatch {
+                version_cur: version,
+                version_new: version_src,
+            });
         }
         self.0.lock().move_element_here_at(
             self.downgrade(),
@@ -641,8 +641,8 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// let packages = file.root_element().create_sub_element(ElementName::ArPackages)?;
-    /// file.root_element().remove_sub_element(packages)?;
+    /// let packages = project.root_element().create_sub_element(ElementName::ArPackages)?;
+    /// project.root_element().remove_sub_element(packages)?;
     /// # Ok(())
     /// # }
     /// ```
@@ -655,7 +655,7 @@ impl Element {
     ///  - [AutosarDataError::ElementNotFound]: The sub element was not found in this element
     ///  - [AutosarDataError::ShortNameRemovalForbidden]: It is not permitted to remove the SHORT-NAME of identifiable elements since this would result in invalid data
     pub fn remove_sub_element(&self, sub_element: Element) -> Result<(), AutosarDataError> {
-        let project = self.file().and_then(|f| f.project())?;
+        let project = self.project()?;
         self.0.lock().remove_sub_element(sub_element, &project)
     }
 
@@ -671,7 +671,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let elements = file.root_element().create_sub_element(ElementName::ArPackages)
+    /// # let elements = project.root_element().create_sub_element(ElementName::ArPackages)
     /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))
     /// #   .and_then(|e| e.create_sub_element(ElementName::Elements))?;
     /// # let ref_element = elements.create_named_sub_element(ElementName::System, "System")
@@ -699,9 +699,8 @@ impl Element {
             let new_ref = target.path()?;
             // it must be possible to use the name of the referenced element name as an enum item in the dest attribute of the reference
             if let Ok(enum_item) = EnumItem::from_str(target.element_name().to_str()) {
-                let file = self.file()?;
-                let project = file.project()?;
-                let version = file.version();
+                let project = self.project()?;
+                let version = self.min_version()?;
                 let mut element = self.0.lock();
                 // set the DEST attribute first - this could fail if the target element has the wrong type
                 if element
@@ -744,7 +743,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let elements = file.root_element().create_sub_element(ElementName::ArPackages)
+    /// # let elements = project.root_element().create_sub_element(ElementName::ArPackages)
     /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))
     /// #   .and_then(|e| e.create_sub_element(ElementName::Elements))?;
     /// # let ref_element = elements.create_named_sub_element(ElementName::System, "System")
@@ -769,7 +768,7 @@ impl Element {
     pub fn get_reference_target(&self) -> Result {
         if self.is_reference() {
             if let Some(CharacterData::String(reference)) = self.character_data() {
-                let project = self.file().and_then(|file| file.project())?;
+                let project = self.project()?;
                 let target_elem = project
                     .get_element_by_path(&reference)
                     .ok_or(AutosarDataError::InvalidReference)?;
@@ -801,7 +800,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element().create_sub_element(ElementName::ArPackages)
+    /// # let element = project.root_element().create_sub_element(ElementName::ArPackages)
     /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))?
     /// #   .get_sub_element(ElementName::ShortName).unwrap();
     /// element.set_character_data(CharacterData::String("value".to_string()))?;
@@ -819,8 +818,8 @@ impl Element {
         let elemtype = self.elemtype();
         if elemtype.content_mode() == ContentMode::Characters {
             if let Some(cdata_spec) = elemtype.chardata_spec() {
-                let project = self.file().and_then(|file| file.project())?;
-                let version = self.file().map(|f| f.version())?;
+                let project = self.project()?;
+                let version = self.min_version()?;
                 if CharacterData::check_value(&chardata, cdata_spec, version) {
                     // if this is a SHORT-NAME element a whole lot of handling is needed in order to unbreak all the cross references
                     let mut prev_path = None;
@@ -887,7 +886,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element().create_sub_element(ElementName::ArPackages)
+    /// # let element = project.root_element().create_sub_element(ElementName::ArPackages)
     /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))
     /// #   .and_then(|e| e.create_sub_element(ElementName::Elements))
     /// #   .and_then(|e| e.create_named_sub_element(ElementName::System, "System"))
@@ -913,7 +912,7 @@ impl Element {
             } else {
                 if self.character_data().is_some() {
                     if self.is_reference() {
-                        let project = self.file().and_then(|file| file.project())?;
+                        let project = self.project()?;
                         if let Some(CharacterData::String(reference)) = self.character_data() {
                             project.remove_reference_origin(&reference, self.downgrade())
                         }
@@ -939,7 +938,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element().create_sub_element(ElementName::ArPackages)
+    /// # let element = project.root_element().create_sub_element(ElementName::ArPackages)
     /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))?;
     /// // mixed content elements are primarily used for documentation and description
     /// let desc = element.create_sub_element(ElementName::Desc)?;
@@ -981,7 +980,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element().create_sub_element(ElementName::ArPackages)
+    /// # let element = project.root_element().create_sub_element(ElementName::ArPackages)
     /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))
     /// #   .and_then(|e| e.create_sub_element(ElementName::Desc))
     /// #   .and_then(|e| e.create_sub_element(ElementName::L2))?;
@@ -1021,7 +1020,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element().create_sub_element(ElementName::ArPackages)
+    /// # let element = project.root_element().create_sub_element(ElementName::ArPackages)
     /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))?
     /// #   .get_sub_element(ElementName::ShortName).unwrap();
     /// match element.character_data() {
@@ -1050,7 +1049,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element();
+    /// # let element = project.root_element();
     /// for content_item in element.content() {
     ///     match content_item {
     ///         ElementContent::CharacterData(data) => {},
@@ -1075,7 +1074,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element();
+    /// # let element = project.root_element();
     /// let weak_element = element.downgrade();
     /// ```
     pub fn downgrade(&self) -> WeakElement {
@@ -1090,7 +1089,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element();
+    /// # let element = project.root_element();
     /// for sub_element in element.sub_elements() {
     ///     // ...
     /// }
@@ -1110,7 +1109,7 @@ impl Element {
     /// # fn main() -> Result<(), AutosarDataError> {
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let pkg = file.root_element().create_sub_element(ElementName::ArPackages)
+    /// # let pkg = project.root_element().create_sub_element(ElementName::ArPackages)
     /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))?;
     /// let element = pkg.get_sub_element(ElementName::ShortName).unwrap();
     /// assert_eq!(element.element_name(), ElementName::ShortName);
@@ -1142,7 +1141,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element();
+    /// # let element = project.root_element();
     /// for (depth, elem) in element.elements_dfs() {
     ///     // ...
     /// }
@@ -1159,7 +1158,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element();
+    /// # let element = project.root_element();
     /// for attribute in element.attributes() {
     ///     println!("{} = {}", attribute.attrname, attribute.content);
     /// }
@@ -1179,7 +1178,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// let value = file.root_element().attribute_value(AttributeName::xsiSchemalocation);
+    /// let value = project.root_element().attribute_value(AttributeName::xsiSchemalocation);
     /// ```
     pub fn attribute_value(&self, attrname: AttributeName) -> Option {
         self.0.lock().attribute_value(attrname)
@@ -1193,7 +1192,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element();
+    /// # let element = project.root_element();
     /// let value = element.attribute_string(AttributeName::Dest);
     /// ```
     pub fn attribute_string(&self, attrname: AttributeName) -> Option {
@@ -1210,7 +1209,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element();
+    /// # let element = project.root_element();
     /// let result = element.set_attribute(AttributeName::S, CharacterData::String("1234-5678".to_string()));
     /// # assert!(result.is_ok());
     /// ```
@@ -1221,7 +1220,7 @@ impl Element {
     ///  - [AutosarDataError::InvalidAttribute]: The AttributeName is not valid for this element
     ///  - [AutosarDataError::InvalidAttributeValue]: The value is not valid for this attribute in this element
     pub fn set_attribute(&self, attrname: AttributeName, value: CharacterData) -> Result<(), AutosarDataError> {
-        let version = self.file().map(|f| f.version())?;
+        let version = self.min_version()?;
         self.0.lock().set_attribute_internal(attrname, value, version)
     }
 
@@ -1235,7 +1234,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element();
+    /// # let element = project.root_element();
     /// let result = element.set_attribute_string(AttributeName::T, "2022-01-31T13:59:59Z");
     /// # assert!(result.is_ok());
     /// ```
@@ -1246,7 +1245,7 @@ impl Element {
     ///  - [AutosarDataError::InvalidAttribute]: The AttributeName is not valid for this element
     ///  - [AutosarDataError::InvalidAttributeValue]: The value is not valid for this attribute in this element
     pub fn set_attribute_string(&self, attrname: AttributeName, stringvalue: &str) -> Result<(), AutosarDataError> {
-        let version = self.file().map(|f| f.version())?;
+        let version = self.min_version()?;
         self.0.lock().set_attribute_string(attrname, stringvalue, version)
     }
 
@@ -1260,7 +1259,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// let result = file.root_element().remove_attribute(AttributeName::xsiSchemalocation);
+    /// let result = project.root_element().remove_attribute(AttributeName::xsiSchemalocation);
     /// // xsiSchemalocation exists in the AUTOSAR element, but it is mandatory and cannot be removed
     /// assert_eq!(result, false);
     /// ```
@@ -1281,7 +1280,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element();
+    /// # let element = project.root_element();
     /// element.sort();
     /// ```
     pub fn sort(&self) {
@@ -1292,11 +1291,17 @@ impl Element {
     pub(crate) fn get_sort_key(&self) -> String {
         if self.is_identifiable() {
             self.item_name().unwrap_or("--INVALID--".to_string())
+        } else if let Some(defref_string) = self
+            .get_sub_element(ElementName::DefinitionRef)
+            .and_then(|defref| defref.character_data())
+            .and_then(|cdata| cdata.string_value())
+        {
+            defref_string
         } else {
             let mut outstring = String::new();
             for content_item in self.content() {
                 match content_item {
-                    ElementContent::Element(e) => e.serialize_internal(&mut outstring, 0, true),
+                    ElementContent::Element(e) => e.serialize_internal(&mut outstring, 0, true, None),
                     ElementContent::CharacterData(cdata) => cdata.serialize_internal(&mut outstring),
                 }
             }
@@ -1314,18 +1319,24 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element();
+    /// # let element = project.root_element();
     /// let text = element.serialize();
     /// ```
     pub fn serialize(&self) -> String {
         let mut outstring = String::new();
 
-        self.serialize_internal(&mut outstring, 0, false);
+        self.serialize_internal(&mut outstring, 0, false, None);
 
         outstring
     }
 
-    pub(crate) fn serialize_internal(&self, outstring: &mut String, indent: usize, inline: bool) {
+    pub(crate) fn serialize_internal(
+        &self,
+        outstring: &mut String,
+        indent: usize,
+        inline: bool,
+        for_file: Option,
+    ) {
         let element_name = self.element_name().to_str();
 
         // write the opening tag on a new line and indent it
@@ -1338,12 +1349,17 @@ impl Element {
             outstring.push_str(element_name);
             self.serialize_attributes(outstring);
             outstring.push('>');
-    
+
             match self.content_type() {
                 ContentType::Elements => {
                     // serialize each sub-element
                     for subelem in self.sub_elements() {
-                        subelem.serialize_internal(outstring, indent + 1, false);
+                        if for_file.is_none()
+                            || subelem.0.lock().file_membership.is_empty()
+                            || subelem.0.lock().file_membership.contains(for_file.as_ref().unwrap())
+                        {
+                            subelem.serialize_internal(outstring, indent + 1, false, for_file.clone());
+                        }
                     }
                     // put the closing tag on a new line and indent it
                     self.serialize_newline_indent(outstring, indent);
@@ -1357,7 +1373,7 @@ impl Element {
                     if let Some(ElementContent::CharacterData(chardata)) = element.content.get(0) {
                         chardata.serialize_internal(outstring);
                     }
-    
+
                     // write the closing tag on the same line
                     outstring.push_str(" {
-                                subelem.serialize_internal(outstring, indent + 1, true);
+                                if for_file.is_none()
+                                    || subelem.0.lock().file_membership.is_empty()
+                                    || subelem.0.lock().file_membership.contains(for_file.as_ref().unwrap())
+                                {
+                                    subelem.serialize_internal(outstring, indent + 1, true, for_file.clone());
+                                }
                             }
                             ElementContent::CharacterData(chardata) => {
                                 chardata.serialize_internal(outstring);
@@ -1414,7 +1435,11 @@ impl Element {
     }
 
     /// check if the sub elements and attributes of this element are compatible with some target_version
-    pub(crate) fn check_version_compatibility(&self, target_version: AutosarVersion) -> (Vec, u32) {
+    pub(crate) fn check_version_compatibility(
+        &self,
+        file: &WeakArxmlFile,
+        target_version: AutosarVersion,
+    ) -> (Vec, u32) {
         let mut compat_errors = Vec::new();
         let mut overall_version_mask = u32::MAX;
 
@@ -1456,22 +1481,24 @@ impl Element {
 
         // check the compatibility of all sub-elements
         for sub_element in self.sub_elements() {
-            if let Some((_, indices)) = self
-                .element_type()
-                .find_sub_element(sub_element.element_name(), u32::MAX)
-            {
-                let version_mask = self.element_type().get_sub_element_version_mask(&indices).unwrap();
-                overall_version_mask &= version_mask;
-                if !target_version.compatible(version_mask) {
-                    compat_errors.push(CompatibilityError::IncompatibleElement {
-                        element: sub_element.clone(),
-                        version_mask,
-                    });
-                } else {
-                    let (mut sub_element_errors, sub_element_mask) =
-                        sub_element.check_version_compatibility(target_version);
-                    compat_errors.append(&mut sub_element_errors);
-                    overall_version_mask &= sub_element_mask;
+            if sub_element.0.lock().file_membership.is_empty() || sub_element.0.lock().file_membership.contains(file) {
+                if let Some((_, indices)) = self
+                    .element_type()
+                    .find_sub_element(sub_element.element_name(), u32::MAX)
+                {
+                    let version_mask = self.element_type().get_sub_element_version_mask(&indices).unwrap();
+                    overall_version_mask &= version_mask;
+                    if !target_version.compatible(version_mask) {
+                        compat_errors.push(CompatibilityError::IncompatibleElement {
+                            element: sub_element.clone(),
+                            version_mask,
+                        });
+                    } else {
+                        let (mut sub_element_errors, sub_element_mask) =
+                            sub_element.check_version_compatibility(file, target_version);
+                        compat_errors.append(&mut sub_element_errors);
+                        overall_version_mask &= sub_element_mask;
+                    }
                 }
             }
         }
@@ -1496,7 +1523,7 @@ impl Element {
     /// # use autosar_data::*;
     /// # let project = AutosarProject::new();
     /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-    /// # let element = file.root_element();
+    /// # let element = project.root_element();
     /// for (element_name, is_named, is_allowed) in element.list_valid_sub_elements() {
     ///     // ...
     /// }
@@ -1505,7 +1532,7 @@ impl Element {
         let etype = self.0.lock().elemtype;
         let mut valid_sub_elements = Vec::new();
 
-        if let Ok(version) = self.file().map(|f| f.version()) {
+        if let Ok(version) = self.min_version() {
             for (element_name, _, version_mask, named_mask) in etype.sub_element_spec_iter() {
                 if version.compatible(version_mask) {
                     let named = version.compatible(named_mask);
@@ -1517,6 +1544,108 @@ impl Element {
 
         valid_sub_elements
     }
+
+    /// Return the set of files in which the current element is present
+    ///
+    /// # Return Value
+    ///
+    /// A tuple (bool, HashSet); if the bool value is true, then the file set is stored in this element, otherwise it is inherited from a parent element.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # use autosar_data::*;
+    /// # fn main() -> Result<(), AutosarDataError> {
+    /// # let project = AutosarProject::new();
+    /// # let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
+    /// # let element = project.root_element();
+    /// let (inherited, file_membership) = element.file_membership()?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    /// # Possible Errors
+    ///
+    ///  - [AutosarDataError::ItemDeleted]: The current element is in the deleted state and will be freed once the last reference is dropped
+    ///  - [AutosarDataError::ParentElementLocked]: a parent element was locked and did not become available after waiting briefly.
+    ///    The operation was aborted to avoid a deadlock, but can be retried.
+    pub fn file_membership(&self) -> Result<(bool, HashSet), AutosarDataError> {
+        let mut cur_elem_opt = Some(self.clone());
+        while let Some(cur_elem) = &cur_elem_opt {
+            let locked_cur_elem = cur_elem
+                .0
+                .try_lock_for(std::time::Duration::from_millis(10))
+                .ok_or(AutosarDataError::ParentElementLocked)?;
+            if !locked_cur_elem.file_membership.is_empty() {
+                return Ok((cur_elem == self, locked_cur_elem.file_membership.clone()));
+            }
+            drop(locked_cur_elem);
+
+            cur_elem_opt = cur_elem.parent()?;
+        }
+
+        // no file membership info found at any level, so all elements inherit the default membership: all files
+        let fileset: HashSet = self.project()?.files().map(|f| f.downgrade()).collect();
+        Ok((false, fileset))
+    }
+
+    /// return the file membership of this element without trying to get an inherited value
+    pub(crate) fn file_membership_local(&self) -> HashSet {
+        self.0.lock().file_membership.clone()
+    }
+
+    /// set the file membership of an element
+    ///
+    /// The passed set acts as a restriction of the file membership of the parent element.
+    /// This means that the set of a child cannot be greater than that of the parent.
+    ///
+    /// Setting an empty set has a special meaning: it reverts the membership to default,
+    /// i.e. inherited from the parent with no additional restriction
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # use autosar_data::*;
+    /// # use std::collections::HashSet;
+    /// # let project = AutosarProject::new();
+    /// let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
+    /// # let element = project.root_element();
+    /// element.set_file_membership(HashSet::from([file.downgrade()]));
+    /// ```
+    pub fn set_file_membership(&self, file_membership: HashSet) {
+        // find out if the parent is splittable. If the parent is unavaliable, assume
+        // that the caller knows what they're doing and assume it is splittable
+        let parent_splittable = self
+            .parent()
+            .ok()
+            .flatten()
+            .map(|p| p.element_type().splittable())
+            .unwrap_or(u32::MAX);
+        // can always reset the membership to empty = inherited; otherwise the parent must be splittable
+        if file_membership.is_empty() || parent_splittable != 0 {
+            self.0.lock().file_membership = file_membership;
+        }
+    }
+
+    /// return a path that includes non-identifiable elements by their xml names
+    ///
+    /// This function cannot fail completely, it will always collect as much information as possible
+    pub fn xml_path(&self) -> String {
+        self.0.lock().xml_path()
+    }
+
+    /// find the minumum version of all arxml files which contain this element
+    ///
+    /// typicall this reduces to finding out which single file contains the element and returning this version
+    fn min_version(&self) -> Result {
+        let (_, files) = self.file_membership()?;
+        let mut ver = AutosarVersion::LATEST;
+        for f in files.iter().filter_map(|file| file.upgrade()) {
+            if f.version() < ver {
+                ver = f.version();
+            }
+        }
+        Ok(ver)
+    }
 }
 
 /// ElementRaw provides the internal implementation of (almost) all Element operations
@@ -1538,17 +1667,17 @@ impl ElementRaw {
     /// get the parent element of the current element
     pub(crate) fn parent(&self) -> Result, AutosarDataError> {
         match &self.parent {
-            ElementOrFile::Element(parent) => {
+            ElementOrProject::Element(parent) => {
                 // for items that should have a parent, getting it is not allowed to return None
                 let parent = parent.upgrade().ok_or(AutosarDataError::ItemDeleted)?;
                 Ok(Some(parent))
             }
-            ElementOrFile::File(_) => Ok(None),
-            ElementOrFile::None => Err(AutosarDataError::ItemDeleted),
+            ElementOrProject::Project(_) => Ok(None),
+            ElementOrProject::None => Err(AutosarDataError::ItemDeleted),
         }
     }
 
-    pub(crate) fn set_parent(&mut self, new_parent: ElementOrFile) {
+    pub(crate) fn set_parent(&mut self, new_parent: ElementOrProject) {
         self.parent = new_parent;
     }
 
@@ -1640,7 +1769,9 @@ impl ElementRaw {
 
             Ok(())
         } else {
-            Err(AutosarDataError::ElementNotIdentifiable)
+            Err(AutosarDataError::ElementNotIdentifiable {
+                xmlpath: self.xml_path(),
+            })
         }
     }
 
@@ -1665,7 +1796,9 @@ impl ElementRaw {
         if self.is_identifiable() {
             self.path_unchecked()
         } else {
-            Err(AutosarDataError::ElementNotIdentifiable)
+            Err(AutosarDataError::ElementNotIdentifiable {
+                xmlpath: self.xml_path(),
+            })
         }
     }
 
@@ -1694,6 +1827,65 @@ impl ElementRaw {
         Ok(path)
     }
 
+    fn xml_path(&self) -> String {
+        let mut path_components = vec![];
+
+        if let Some(name) = self.item_name() {
+            path_components.push(name.to_string());
+        } else {
+            path_components.push(format!("<{}>", self.element_name()));
+        }
+
+        if let Ok(mut cur_elem_opt) = self.parent() {
+            while let Some(cur_elem) = cur_elem_opt {
+                if let Some(locked_cur_elem) = cur_elem.0.try_lock_for(std::time::Duration::from_millis(10)) {
+                    // build a string for this path component
+                    if locked_cur_elem.elemtype.is_named() {
+                        if let Some(item_name) = locked_cur_elem.item_name() {
+                            path_components.push(item_name.to_string());
+                        } else {
+                            path_components.push(format!("<{}>(name missing)", locked_cur_elem.element_name()));
+                        }
+                    } else {
+                        path_components.push(format!("<{}>", locked_cur_elem.element_name()));
+                    }
+                    // try to get the parent of the current element
+                    match &locked_cur_elem.parent {
+                        ElementOrProject::Element(weak_parent) => {
+                            if let Some(parent) = weak_parent.upgrade() {
+                                cur_elem_opt = Some(parent);
+                            } else {
+                                // failed to upgrade the weak ref to the parent - this can't happen in single threaded execution,
+                                // but it could be possible when racing remove_element on another thread
+                                path_components.push("(DELETED)".to_string());
+                                cur_elem_opt = None;
+                            }
+                        }
+                        ElementOrProject::Project(_) => {
+                            // done
+                            cur_elem_opt = None;
+                        }
+                        ElementOrProject::None => {
+                            // this happens if xml_path is called on an element which has been deleted
+                            // remove_element sets the parent to None
+                            path_components.push("(DELETED)".to_string());
+                            cur_elem_opt = None;
+                        }
+                    }
+                } else {
+                    path_components.push("(LOCKED)".to_string());
+                    cur_elem_opt = None;
+                }
+            }
+        } else {
+            path_components.push("(DELETED)".to_string());
+        }
+
+        path_components.push(String::new());
+        path_components.reverse();
+        path_components.join("/")
+    }
+
     /// Create a sub element at a suitable insertion position
     ///
     /// The given ElementName must be allowed on a sub element in this element, taking into account any sub elements that may already exist.
@@ -1744,11 +1936,12 @@ impl ElementRaw {
             Err(AutosarDataError::ItemNameRequired)
         } else {
             let sub_element = Element(Arc::new(Mutex::new(ElementRaw {
-                parent: ElementOrFile::Element(self_weak),
+                parent: ElementOrProject::Element(self_weak),
                 elemname: element_name,
                 elemtype,
                 content: smallvec![],
                 attributes: smallvec![],
+                file_membership: HashSet::with_capacity(0),
             })));
             self.content
                 .insert(position, ElementContent::Element(sub_element.clone()));
@@ -1834,11 +2027,12 @@ impl ElementRaw {
 
             // create the new element
             let sub_element = Element(Arc::new(Mutex::new(ElementRaw {
-                parent: ElementOrFile::Element(self_weak),
+                parent: ElementOrProject::Element(self_weak),
                 elemname: element_name,
                 elemtype,
                 content: smallvec![],
                 attributes: smallvec![],
+                file_membership: HashSet::with_capacity(0),
             })));
             self.content
                 .insert(position, ElementContent::Element(sub_element.clone()));
@@ -1852,7 +2046,9 @@ impl ElementRaw {
             project.add_identifiable(path, sub_element.downgrade());
             Ok(sub_element)
         } else {
-            Err(AutosarDataError::ElementNotIdentifiable)
+            Err(AutosarDataError::ElementNotIdentifiable {
+                xmlpath: self.xml_path(),
+            })
         }
     }
 
@@ -1910,7 +2106,7 @@ impl ElementRaw {
         let path = self.path_unchecked()?;
 
         // set the parent of the newelem - the methods path(), containing_file(), etc become available on newelem
-        newelem.set_parent(ElementOrFile::Element(self_weak));
+        newelem.set_parent(ElementOrProject::Element(self_weak));
         if newelem.is_identifiable() {
             newelem.0.lock().make_unique_item_name(project, &path)?;
         }
@@ -1952,7 +2148,8 @@ impl ElementRaw {
             elemtype: self.elemtype,
             content: SmallVec::with_capacity(self.content.len()),
             attributes: SmallVec::with_capacity(self.attributes.len()),
-            parent: ElementOrFile::None,
+            parent: ElementOrProject::None,
+            file_membership: HashSet::with_capacity(0),
         })));
 
         {
@@ -1964,10 +2161,11 @@ impl ElementRaw {
                     spec: cdataspec,
                     required,
                     version: attr_version_mask,
-                } = self
-                    .elemtype
-                    .find_attribute_spec(attribute.attrname)
-                    .ok_or(AutosarDataError::VersionIncompatible)?;
+                } = self.elemtype.find_attribute_spec(attribute.attrname).ok_or(
+                    AutosarDataError::VersionIncompatibleData {
+                        version: target_version,
+                    },
+                )?;
                 // check if the attribute is compatible with the target version
                 if target_version.compatible(attr_version_mask)
                     && attribute
@@ -1977,7 +2175,9 @@ impl ElementRaw {
                 {
                     copy.attributes.push(attribute.clone());
                 } else if required {
-                    return Err(AutosarDataError::VersionIncompatible);
+                    return Err(AutosarDataError::VersionIncompatibleData {
+                        version: target_version,
+                    });
                 } else {
                     // no action, the attribute is not compatible, but it's not required either
                 }
@@ -1995,7 +2195,7 @@ impl ElementRaw {
                             .is_some()
                         {
                             if let Ok(copied_sub_elem) = sub_elem.0.lock().deep_copy(target_version) {
-                                copied_sub_elem.0.lock().parent = ElementOrFile::Element(copy_wrapped.downgrade());
+                                copied_sub_elem.0.lock().parent = ElementOrProject::Element(copy_wrapped.downgrade());
                                 copy.content.push(ElementContent::Element(copied_sub_elem));
                             }
                         }
@@ -2012,7 +2212,9 @@ impl ElementRaw {
 
     /// make_unique_item_name ensures that a copied element has a unique name
     fn make_unique_item_name(&self, project: &AutosarProject, parent_path: &str) -> Result {
-        let orig_name = self.item_name().ok_or(AutosarDataError::ElementNotIdentifiable)?;
+        let orig_name = self.item_name().ok_or(AutosarDataError::ElementNotIdentifiable {
+            xmlpath: self.xml_path(),
+        })?;
         let mut name = orig_name.clone();
         let mut counter = 1;
 
@@ -2156,7 +2358,7 @@ impl ElementRaw {
         // check if self (target of the move) is a sub element of new_element
         // if it is, then the move is not allowed
         let mut wrapped_parent = self.parent.clone();
-        while let ElementOrFile::Element(weak_parent) = wrapped_parent {
+        while let ElementOrProject::Element(weak_parent) = wrapped_parent {
             let parent = weak_parent.upgrade().ok_or(AutosarDataError::ItemDeleted)?;
             if parent == *move_element {
                 return Err(AutosarDataError::ForbiddenMoveToSubElement);
@@ -2167,7 +2369,16 @@ impl ElementRaw {
         let src_parent = move_element.parent()?.ok_or(AutosarDataError::InvalidSubElement)?;
 
         // collect the paths of all identifiable elements under new_element before moving it
-        let original_paths: Vec = move_element.elements_dfs().filter_map(|(_, e)| e.path().ok()).collect();
+        let original_paths: Vec = move_element
+            .elements_dfs()
+            .filter_map(|(_, e)| {
+                if e.element_type().is_named() {
+                    e.path().ok()
+                } else {
+                    None
+                }
+            })
+            .collect();
 
         let src_path_prefix = move_element.0.lock().path_unchecked()?;
         let dest_path_prefix = self.path_unchecked()?;
@@ -2196,7 +2407,7 @@ impl ElementRaw {
         }
 
         // set the parent of the new element to the current element
-        move_element.0.lock().parent = ElementOrFile::Element(self_weak);
+        move_element.0.lock().parent = ElementOrProject::Element(self_weak);
         let dest_path = if move_element.is_identifiable() {
             let new_name = move_element
                 .0
@@ -2268,7 +2479,13 @@ impl ElementRaw {
         // collect the paths of all identifiable elements under move_element before moving it
         let original_paths: FxHashMap = move_element
             .elements_dfs()
-            .filter_map(|(_, e)| e.path().ok().map(|path| (path, e)))
+            .filter_map(|(_, e)| {
+                if e.element_type().is_named() {
+                    e.path().ok().map(|path| (path, e))
+                } else {
+                    None
+                }
+            })
             .collect();
         // collect all reference targets and referring elements under move_element
         let original_refs: Vec<(String, Element)> = move_element
@@ -2310,7 +2527,7 @@ impl ElementRaw {
         }
 
         // set the parent of the new element to the current element
-        move_element.0.lock().parent = ElementOrFile::Element(self_weak);
+        move_element.0.lock().parent = ElementOrProject::Element(self_weak);
         let dest_path = if move_element.is_identifiable() {
             let new_name = move_element
                 .0
@@ -2352,7 +2569,7 @@ impl ElementRaw {
     }
 
     /// find the upper and lower bound on the insert position for a sub element
-    fn find_element_insert_pos(
+    pub(crate) fn find_element_insert_pos(
         &self,
         element_name: ElementName,
         version: AutosarVersion,
@@ -2504,7 +2721,7 @@ impl ElementRaw {
             }
         }
         self.content.clear();
-        self.parent = ElementOrFile::None;
+        self.parent = ElementOrProject::None;
     }
 
     /// set the character data of this element
@@ -2712,6 +2929,8 @@ impl PartialEq for Element {
     }
 }
 
+impl Eq for Element {}
+
 impl WeakElement {
     /// try to get a strong reference to the [Element]
     pub fn upgrade(&self) -> Option {
@@ -2725,6 +2944,7 @@ impl PartialEq for WeakElement {
     }
 }
 
+impl Eq for WeakElement {}
 
 impl ElementContent {
     /// returns the element contained inside this ElementContent, or None if the content is CharacterData
@@ -2746,7 +2966,6 @@ impl ElementContent {
     }
 }
 
-
 #[cfg(test)]
 mod test {
     use crate::*;
@@ -2765,9 +2984,9 @@ mod test {
     fn element_creation() {
         let project = AutosarProject::new();
         project
-            .load_named_arxml_buffer(BASIC_AUTOSAR_FILE.as_bytes(), &OsString::from("test.arxml"), true)
+            .load_named_arxml_buffer(BASIC_AUTOSAR_FILE.as_bytes(), OsString::from("test.arxml"), true)
             .unwrap();
-        let el_autosar = project.files().next().unwrap().root_element();
+        let el_autosar = project.root_element();
         let el_ar_package = project.get_element_by_path("/TestPackage").unwrap();
 
         let el_elements = el_ar_package.create_sub_element(ElementName::Elements).unwrap();
@@ -2891,10 +3110,10 @@ mod test {
     #[test]
     fn element_rename() {
         let project = AutosarProject::new();
-        let file = project
+        project
             .create_file("test.arxml", AutosarVersion::Autosar_00050)
             .unwrap();
-        let el_autosar = file.root_element();
+        let el_autosar = project.root_element();
         let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
         let el_ar_package = el_ar_packages
             .create_named_sub_element(ElementName::ArPackage, "Package")
@@ -2975,7 +3194,7 @@ mod test {
     fn element_copy() {
         let project = AutosarProject::new();
         project
-            .load_named_arxml_buffer(BASIC_AUTOSAR_FILE.as_bytes(), &OsString::from("test.arxml"), true)
+            .load_named_arxml_buffer(BASIC_AUTOSAR_FILE.as_bytes(), OsString::from("test.arxml"), true)
             .unwrap();
         let el_ar_package = project.get_element_by_path("/TestPackage").unwrap();
         el_ar_package
@@ -3008,18 +3227,21 @@ mod test {
             .unwrap();
 
         let project2 = AutosarProject::new();
-        let file = project2
+        project2
             .create_file(OsString::from("test.arxml"), AutosarVersion::Autosar_00044)
             .unwrap();
 
         // it should not be possible to create an AR-PACKAGE element directly in the AUTOSAR element by copying data
-        let result = file.root_element().create_copied_sub_element(&el_ar_package);
+        let result = project2.root_element().create_copied_sub_element(&el_ar_package);
         assert!(result.is_err());
 
         // create an AR-PACKAGES element and copy the data there. This should succeed.
         // the copied data shoud contain the COMPU-METHOD, but not the DDS-SERVICE-INSTANCE-TO-MACHINE-MAPPING
         // because the latter was specified in Adaptive 18-03 (Autosar_00045) and is not valid in Autosar_00044
-        let el_ar_packages2 = file.root_element().create_sub_element(ElementName::ArPackages).unwrap();
+        let el_ar_packages2 = project2
+            .root_element()
+            .create_sub_element(ElementName::ArPackages)
+            .unwrap();
         el_ar_packages2.create_copied_sub_element(&el_ar_package).unwrap();
 
         // it should be possible to look up the copied compu-method by its path
@@ -3055,7 +3277,7 @@ mod test {
     fn element_deletion() {
         let project = AutosarProject::new();
         project
-            .load_named_arxml_buffer(BASIC_AUTOSAR_FILE.as_bytes(), &OsString::from("test.arxml"), true)
+            .load_named_arxml_buffer(BASIC_AUTOSAR_FILE.as_bytes(), OsString::from("test.arxml"), true)
             .unwrap();
         let el_ar_package = project.get_element_by_path("/TestPackage").unwrap();
         let el_short_name = el_ar_package.get_sub_element(ElementName::ShortName).unwrap();
@@ -3085,47 +3307,21 @@ mod test {
     #[test]
     fn element_type() {
         let project = AutosarProject::new();
-        let file = project
+        project
             .create_file("test.arxml", AutosarVersion::Autosar_00050)
             .unwrap();
-        let el_autosar = file.root_element();
+        let el_autosar = project.root_element();
 
         assert_eq!(el_autosar.element_type(), ElementType::ROOT);
     }
 
-    #[test]
-    fn file() {
-        let project = AutosarProject::new();
-        let file = project
-            .create_file("test.arxml", AutosarVersion::Autosar_00050)
-            .unwrap();
-        let el_autosar = file.root_element();
-        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
-
-        // ok case
-        assert!(el_autosar.file().is_ok());
-
-        // invalid reference to a file
-        drop(project);
-        drop(file);
-        assert!(el_autosar.file().is_err());
-
-        // no reference to a file
-        el_autosar.0.lock().parent = ElementOrFile::None;
-        assert!(el_autosar.file().is_err());
-
-        // invalid parent
-        drop(el_autosar);
-        assert!(el_ar_packages.file().is_err());
-    }
-
     #[test]
     fn content_type() {
         let project = AutosarProject::new();
-        let file = project
+        project
             .create_file("test.arxml", AutosarVersion::Autosar_00050)
             .unwrap();
-        let el_autosar = file.root_element();
+        let el_autosar = project.root_element();
         let el_ar_package = el_autosar
             .create_sub_element(ElementName::ArPackages)
             .and_then(|ar_pkgs| ar_pkgs.create_named_sub_element(ElementName::ArPackage, "Package"))
@@ -3163,10 +3359,10 @@ mod test {
     #[test]
     fn attributes() {
         let project = AutosarProject::new();
-        let (file, _) = project
-            .load_named_arxml_buffer(BASIC_AUTOSAR_FILE.as_bytes(), &OsString::from("test.arxml"), true)
+        project
+            .load_named_arxml_buffer(BASIC_AUTOSAR_FILE.as_bytes(), OsString::from("test.arxml"), true)
             .unwrap();
-        let el_autosar = file.root_element();
+        let el_autosar = project.root_element();
         let el_ar_packages = el_autosar.get_sub_element(ElementName::ArPackages).unwrap();
 
         let count = el_autosar.attributes().count();
@@ -3184,15 +3380,15 @@ mod test {
 
         // The attribute S exists and is optional, so it can be removed
         let result = el_autosar.remove_attribute(AttributeName::S);
-        assert_eq!(result, true);
+        assert!(result);
 
         // the attribute xmlns is required and cannot be removed
         let result = el_autosar.remove_attribute(AttributeName::xmlns);
-        assert_eq!(result, false);
+        assert!(!result);
 
         // the attribute ACCESSKEY does not exist in the element AUTOSAR and cannot be removed
         let result = el_autosar.remove_attribute(AttributeName::Accesskey);
-        assert_eq!(result, false);
+        assert!(!result);
 
         // the attribute T is permitted on AUTOSAR and the string is a valid value
         el_autosar
@@ -3231,7 +3427,7 @@ mod test {
     fn mixed_content() {
         let project = AutosarProject::new();
         project
-            .load_named_arxml_buffer(BASIC_AUTOSAR_FILE.as_bytes(), &OsString::from("test.arxml"), true)
+            .load_named_arxml_buffer(BASIC_AUTOSAR_FILE.as_bytes(), OsString::from("test.arxml"), true)
             .unwrap();
         let el_ar_package = project.get_element_by_path("/TestPackage").unwrap();
         let el_long_name = el_ar_package.create_sub_element(ElementName::LongName).unwrap();
@@ -3261,10 +3457,10 @@ mod test {
     fn move_element_position() {
         // move an element to a different position within its parent
         let project = AutosarProject::new();
-        let file = project
+        project
             .create_file("test.arxml", AutosarVersion::Autosar_00050)
             .unwrap();
-        let el_autosar = file.root_element();
+        let el_autosar = project.root_element();
         let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
         let pkg1 = el_ar_packages
             .create_named_sub_element(ElementName::ArPackage, "Pkg1")
@@ -3311,10 +3507,10 @@ mod test {
     fn move_element_local() {
         // move an element within the same project
         let project = AutosarProject::new();
-        let file = project
+        project
             .create_file("test.arxml", AutosarVersion::Autosar_00050)
             .unwrap();
-        let el_autosar = file.root_element();
+        let el_autosar = project.root_element();
         let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
         let el_pkg1 = el_ar_packages
             .create_named_sub_element(ElementName::ArPackage, "Pkg1")
@@ -3380,10 +3576,10 @@ mod test {
     fn move_element_full() {
         // move an element between two projects
         let project1 = AutosarProject::new();
-        let file = project1
+        project1
             .create_file("test1.arxml", AutosarVersion::Autosar_00050)
             .unwrap();
-        let el_autosar = file.root_element();
+        let el_autosar = project1.root_element();
         let el_ar_packages1 = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
         let el_pkg1 = el_ar_packages1
             .create_named_sub_element(ElementName::ArPackage, "Pkg1")
@@ -3405,10 +3601,10 @@ mod test {
         el_fibex_element_ref.set_reference_target(&el_ecu_instance).unwrap();
 
         let project2 = AutosarProject::new();
-        let file2 = project2
+        project2
             .create_file("test2.arxml", AutosarVersion::Autosar_00050)
             .unwrap();
-        let el_autosar2 = file2.root_element();
+        let el_autosar2 = project2.root_element();
         let el_ar_packages2 = el_autosar2.create_sub_element(ElementName::ArPackages).unwrap();
 
         // move a named element
@@ -3433,10 +3629,10 @@ mod test {
 
         // can't move between files with different versions
         let project3 = AutosarProject::new();
-        let file3 = project3
+        project3
             .create_file("test2.arxml", AutosarVersion::Autosar_4_3_0)
             .unwrap();
-        let el_autosar3 = file3.root_element();
+        let el_autosar3 = project3.root_element();
         assert!(el_autosar3.move_element_here(&el_ar_packages2).is_err());
         assert!(el_autosar3.move_element_here_at(&el_ar_packages2, 0).is_err());
     }
@@ -3444,10 +3640,10 @@ mod test {
     #[test]
     fn get_set_reference_target() {
         let project = AutosarProject::new();
-        let file = project
+        project
             .create_file("text.arxml", AutosarVersion::Autosar_00050)
             .unwrap();
-        let el_autosar = file.root_element();
+        let el_autosar = project.root_element();
         let el_ar_package = el_autosar
             .create_sub_element(ElementName::ArPackages)
             .and_then(|arpkgs| arpkgs.create_named_sub_element(ElementName::ArPackage, "Package"))
@@ -3517,10 +3713,10 @@ mod test {
     #[test]
     fn modify_character_data() {
         let project = AutosarProject::new();
-        let file = project
+        project
             .create_file("text.arxml", AutosarVersion::Autosar_00050)
             .unwrap();
-        let el_autosar = file.root_element();
+        let el_autosar = project.root_element();
         let el_ar_package = el_autosar
             .create_sub_element(ElementName::ArPackages)
             .and_then(|arpkgs| arpkgs.create_named_sub_element(ElementName::ArPackage, "Package"))
@@ -3637,8 +3833,8 @@ mod test {
     #[test]
     fn mixed_character_content() {
         let project = AutosarProject::new();
-        let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-        let el_ar_package = file
+        project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
+        let el_ar_package = project
             .root_element()
             .create_sub_element(ElementName::ArPackages)
             .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))
@@ -3674,8 +3870,8 @@ mod test {
     #[test]
     fn get_sub_element() {
         let project = AutosarProject::new();
-        let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-        let el_autosar = file.root_element();
+        project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
+        let el_autosar = project.root_element();
         let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
         let el_ar_package = el_ar_packages
             .create_named_sub_element(ElementName::ArPackage, "Package")
@@ -3709,13 +3905,13 @@ mod test {
 "#;
         let project = AutosarProject::new();
         project
-            .load_named_arxml_buffer(FILEBUF.as_bytes(), &OsString::from("test"), true)
+            .load_named_arxml_buffer(FILEBUF.as_bytes(), OsString::from("test"), true)
             .unwrap();
-        let file = project.files().next().unwrap();
-        let el_autosar = file.root_element();
+        project.files().next().unwrap();
+        let el_autosar = project.root_element();
 
         let mut outstring = String::from(r#""#);
-        el_autosar.serialize_internal(&mut outstring, 0, false);
+        el_autosar.serialize_internal(&mut outstring, 0, false, None);
 
         assert_eq!(FILEBUF, outstring);
     }
@@ -3723,10 +3919,10 @@ mod test {
     #[test]
     fn list_valid_sub_elements() {
         let project = AutosarProject::new();
-        let file = project
+        project
             .create_file("test.arxml", AutosarVersion::Autosar_4_3_0)
             .unwrap();
-        let el_autosar = file.root_element();
+        let el_autosar = project.root_element();
         let el_elements = el_autosar
             .create_sub_element(ElementName::ArPackages)
             .and_then(|el| el.create_named_sub_element(ElementName::ArPackage, "Package"))
@@ -3758,13 +3954,14 @@ mod test {
   
 "#;
         let project = AutosarProject::new();
-        project
+        let (file, _) = project
             .load_named_arxml_buffer(FILEBUF.as_bytes(), &OsString::from("test"), true)
             .unwrap();
-        let file = project.files().next().unwrap();
-        let el_autosar = file.root_element();
+        project.files().next().unwrap();
+        let el_autosar = project.root_element();
 
-        let (compat_errors, _) = el_autosar.check_version_compatibility(AutosarVersion::Autosar_4_3_0);
+        let (compat_errors, _) =
+            el_autosar.check_version_compatibility(&file.downgrade(), AutosarVersion::Autosar_4_3_0);
         assert_eq!(compat_errors.len(), 3);
 
         for ce in compat_errors {
@@ -3787,8 +3984,8 @@ mod test {
     #[test]
     fn find_element_insert_pos() {
         let project = AutosarProject::new();
-        let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-        let el_autosar = file.root_element();
+        project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
+        let el_autosar = project.root_element();
         let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
         let el_ar_package = el_ar_packages
             .create_named_sub_element(ElementName::ArPackage, "Pkg")
@@ -3813,23 +4010,128 @@ mod test {
     #[test]
     fn sort() {
         let project = AutosarProject::new();
-        let file = project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
-        let el_autosar = file.root_element();
+        project.create_file("test", AutosarVersion::Autosar_00050).unwrap();
+        let el_autosar = project.root_element();
         let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
-        let el_ar_package = el_ar_packages
+        let el_ar_package1 = el_ar_packages
             .create_named_sub_element(ElementName::ArPackage, "Z")
             .unwrap();
-        el_ar_packages
+        let el_ar_package2 = el_ar_packages
             .create_named_sub_element(ElementName::ArPackage, "A")
             .unwrap();
-        el_ar_package.create_sub_element(ElementName::Elements).unwrap();
-        el_ar_package.create_sub_element(ElementName::AdminData).unwrap();
+        let el_elements = el_ar_package1.create_sub_element(ElementName::Elements).unwrap();
+        el_ar_package1.create_sub_element(ElementName::AdminData).unwrap();
+        // create some bsw values to sort inside el_ar_package1 "Z"
+        let el_emcv = el_elements
+            .create_named_sub_element(ElementName::EcucModuleConfigurationValues, "Config")
+            .unwrap();
+        let el_containers = el_emcv.create_sub_element(ElementName::Containers).unwrap();
+        let el_ecv = el_containers
+            .create_named_sub_element(ElementName::EcucContainerValue, "ConfigValues")
+            .unwrap();
+        let el_paramvalues = el_ecv.create_sub_element(ElementName::ParameterValues).unwrap();
+        // first bsw value
+        let el_value1 = el_paramvalues
+            .create_sub_element(ElementName::EcucNumericalParamValue)
+            .unwrap();
+        let el_defref1 = el_value1.create_sub_element(ElementName::DefinitionRef).unwrap();
+        el_defref1
+            .set_attribute(AttributeName::Dest, CharacterData::Enum(EnumItem::EcucBooleanParamDef))
+            .unwrap();
+        el_defref1
+            .set_character_data(CharacterData::String("/DefRef_999".to_string()))
+            .unwrap();
+        // second bsw value
+        let el_value2 = el_paramvalues
+            .create_sub_element(ElementName::EcucNumericalParamValue)
+            .unwrap();
+        let el_defref2 = el_value2.create_sub_element(ElementName::DefinitionRef).unwrap();
+        el_defref2
+            .set_attribute(AttributeName::Dest, CharacterData::Enum(EnumItem::EcucBooleanParamDef))
+            .unwrap();
+        el_defref2
+            .set_character_data(CharacterData::String("/DefRef_111".to_string()))
+            .unwrap();
+        // Create some misc value sto sort inside el_ar_package2 "A"
+        let el_elements2 = el_ar_package2.create_sub_element(ElementName::Elements).unwrap();
+        let el_system = el_elements2
+            .create_named_sub_element(ElementName::System, "System")
+            .unwrap();
+        let el_fibex_elements = el_system.create_sub_element(ElementName::FibexElements).unwrap();
+        let el_fibex_element1 = el_fibex_elements
+            .create_sub_element(ElementName::FibexElementRefConditional)
+            .unwrap();
+        let el_fibex_element_ref1 = el_fibex_element1
+            .create_sub_element(ElementName::FibexElementRef)
+            .unwrap();
+        el_fibex_element_ref1
+            .set_attribute(AttributeName::Dest, CharacterData::Enum(EnumItem::ISignal))
+            .unwrap();
+        el_fibex_element_ref1
+            .set_character_data(CharacterData::String("/ZZZZZ".to_string()))
+            .unwrap();
+        let el_fibex_element2 = el_fibex_elements
+            .create_sub_element(ElementName::FibexElementRefConditional)
+            .unwrap();
+        let el_fibex_element_ref2 = el_fibex_element2
+            .create_sub_element(ElementName::FibexElementRef)
+            .unwrap();
+        el_fibex_element_ref2
+            .set_attribute(AttributeName::Dest, CharacterData::Enum(EnumItem::ISignal))
+            .unwrap();
+        el_fibex_element_ref2
+            .set_character_data(CharacterData::String("/AAAAA".to_string()))
+            .unwrap();
 
-        file.sort();
+        project.sort();
+        // validae that identifiable elements have been sorted
         let mut iter = el_ar_packages.sub_elements();
         let item1 = iter.next().unwrap();
         let item2 = iter.next().unwrap();
         assert_eq!(item1.item_name().unwrap(), "A");
         assert_eq!(item2.item_name().unwrap(), "Z");
+
+        // validate that BSW parameter values have been sorted
+        let mut iter = el_paramvalues.sub_elements();
+        let item1 = iter.next().unwrap();
+        let item2 = iter.next().unwrap();
+        assert_eq!(item1, el_value2);
+        assert_eq!(item2, el_value1);
+
+        // validate that the misc elements (FIBEX-ELEMENT-REF-CONDITIONAL) have been sorted
+        let mut iter = el_fibex_elements.sub_elements();
+        let item1 = iter.next().unwrap();
+        let item2 = iter.next().unwrap();
+        assert_eq!(item1, el_fibex_element2);
+        assert_eq!(item2, el_fibex_element1);
+    }
+
+    #[test]
+    fn file_membership() {
+        let project = AutosarProject::new();
+        let file1 = project.create_file("test_1", AutosarVersion::Autosar_00050).unwrap();
+        let file2 = project.create_file("test_2", AutosarVersion::Autosar_00050).unwrap();
+        let el_autosar = project.root_element();
+        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
+        let el_ar_package = el_ar_packages
+            .create_named_sub_element(ElementName::ArPackage, "Pkg")
+            .unwrap();
+
+        let fm: HashSet = vec![file1.downgrade()].iter().cloned().collect();
+        // setting the file membership of el_ar_packages should fail
+        // its parent is not splittable, so this is not allowed
+        el_ar_packages.set_file_membership(fm.clone());
+        let (local, _) = el_ar_package.file_membership().unwrap();
+        assert!(!local);
+
+        // setting the file membership of el_ar_package should succeed
+        // this element is only part of file1, and is only serialized with file1
+        el_ar_package.set_file_membership(fm.clone());
+        let (local, fm2) = el_ar_package.file_membership().unwrap();
+        assert!(local);
+        assert_eq!(fm, fm2);
+        let filetxt1 = file1.serialize().unwrap();
+        let filetxt2 = file2.serialize().unwrap();
+        assert_ne!(filetxt1, filetxt2);
     }
 }
diff --git a/autosar-data/src/iterators.rs b/autosar-data/src/iterators.rs
index 36544bb..6efca81 100644
--- a/autosar-data/src/iterators.rs
+++ b/autosar-data/src/iterators.rs
@@ -80,34 +80,33 @@ impl ElementContentIterator {
     }
 }
 
-pub struct AutosarDataElementsDfsIterator {
-    files_iter: ArxmlFileIterator,
-    current_dfs_iter: Option,
+pub struct ArxmlFileElementsDfsIterator {
+    file: WeakArxmlFile,
+    dfs_iter: ElementsDfsIterator,
 }
 
-impl AutosarDataElementsDfsIterator {
-    pub(crate) fn new(files_iter: ArxmlFileIterator) -> Self {
+impl ArxmlFileElementsDfsIterator {
+    pub(crate) fn new(file: WeakArxmlFile, element: &Element) -> Self {
         Self {
-            files_iter,
-            current_dfs_iter: None,
+            file,
+            dfs_iter: ElementsDfsIterator::new(element),
         }
     }
 }
 
-impl Iterator for AutosarDataElementsDfsIterator {
+impl Iterator for ArxmlFileElementsDfsIterator {
     type Item = (usize, Element);
 
     fn next(&mut self) -> Option {
-        if let Some(dfs_iter) = &mut self.current_dfs_iter {
-            if let Some(result) = dfs_iter.next() {
-                return Some(result);
+        let mut next_element = self.dfs_iter.next();
+        while let Some((depth, elem)) = next_element {
+            let files = elem.file_membership_local();
+            if files.is_empty() || files.contains(&self.file) {
+                return Some((depth, elem));
+            } else {
+                next_element = self.dfs_iter.next_sibling();
             }
         }
-        if let Some(current_file) = &self.files_iter.next() {
-            self.current_dfs_iter = Some(current_file.elements_dfs());
-            return self.next();
-        }
-
         None
     }
 }
@@ -128,6 +127,37 @@ impl ElementsDfsIterator {
             depth: 0,
         }
     }
+
+    pub fn next_sibling(&mut self) -> Option<(usize, Element)> {
+        // descend throgh the hierarchy of dfs_iters
+        if let Some(dfs_iter) = &mut self.current_dfs_iter {
+            // check if the dfs_iter for the "grandchildren" exists. If it does, then descend to the child
+            // otherwise do not descend, because it is time to emit the sibling
+            if dfs_iter.current_dfs_iter.is_some() {
+                if let Some(result) = dfs_iter.next_sibling() {
+                    return Some(result);
+                }
+            }
+        }
+        // switch to next sub_element
+        if let Some(elements_iter) = &mut self.elements_iter {
+            if let Some(next_elem) = elements_iter.next() {
+                self.current_dfs_iter = Some(Box::new(ElementsDfsIterator {
+                    element: next_elem,
+                    elements_iter: None,
+                    current_dfs_iter: None,
+                    depth: self.depth + 1,
+                }));
+                // first call of the dfs_iter should alway yield Some(x), because the first element
+                // to be returned is the same one that was used to construct the dfs iterator
+                return self.next();
+            }
+            return None;
+        }
+        // return the starting element
+        self.elements_iter = Some(self.element.sub_elements());
+        Some((self.depth, self.element.clone()))
+    }
 }
 
 impl Iterator for ElementsDfsIterator {
@@ -222,16 +252,17 @@ mod test {
     use super::*;
 
     #[test]
-    fn test_elements_dfs_iterator() {
+    fn elements_dfs_iterator() {
         let sub_sub_element = Element(Arc::new(Mutex::new(ElementRaw {
-            parent: ElementOrFile::None,
+            parent: ElementOrProject::None,
             elemname: ElementName::ArPackage, // doesn't matter for this test
             elemtype: ElementType::ROOT,      // doesn't matter for this test
             attributes: SmallVec::new(),
             content: SmallVec::new(),
+            file_membership: HashSet::with_capacity(0),
         })));
         let sub_element = Element(Arc::new(Mutex::new(ElementRaw {
-            parent: ElementOrFile::None,
+            parent: ElementOrProject::None,
             elemname: ElementName::ArPackages, // doesn't matter for this test
             elemtype: ElementType::ROOT,       // doesn't matter for this test
             attributes: SmallVec::new(),
@@ -239,9 +270,10 @@ mod test {
                 ElementContent::Element(sub_sub_element.clone()),
                 ElementContent::Element(sub_sub_element.clone())
             ],
+            file_membership: HashSet::with_capacity(0),
         })));
         let element = Element(Arc::new(Mutex::new(ElementRaw {
-            parent: ElementOrFile::None,
+            parent: ElementOrProject::None,
             elemname: ElementName::Autosar, // doesn't matter for this test
             elemtype: ElementType::ROOT,    // doesn't matter for this test
             attributes: SmallVec::new(),
@@ -249,8 +281,31 @@ mod test {
                 ElementContent::Element(sub_element.clone()),
                 ElementContent::Element(sub_element.clone())
             ],
+            file_membership: HashSet::with_capacity(0),
         })));
         let dfs_iter = element.elements_dfs();
         assert_eq!(dfs_iter.count(), 7);
     }
+
+    #[test]
+    fn elements_dfs_next_sibling() {
+        let project = AutosarProject::new();
+        let el_autosar = project.root_element();
+        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
+        let el_ar_package_1 = el_ar_packages.create_named_sub_element(ElementName::ArPackage, "Package1") .unwrap();
+        el_ar_package_1.create_sub_element(ElementName::Elements).unwrap();
+        let el_ar_package_2 = el_ar_packages.create_named_sub_element(ElementName::ArPackage, "Package2") .unwrap();
+        el_ar_package_2.create_sub_element(ElementName::Elements).unwrap();
+
+        let mut dfs_iter = project.elements_dfs();
+        let (_, item) = dfs_iter.next().unwrap();
+        assert_eq!(item, el_autosar);
+        let (_, item) = dfs_iter.next().unwrap();
+        assert_eq!(item, el_ar_packages);
+        let (_, item) = dfs_iter.next().unwrap();
+        assert_eq!(item, el_ar_package_1);
+        // next() would return the element ELEMENTS inside el_ar_package_1, but next_sibling should return el_ar_package_2 instead
+        let (_, item) = dfs_iter.next_sibling().unwrap();
+        assert_eq!(item, el_ar_package_2);
+    }
 }
diff --git a/autosar-data/src/lexer.rs b/autosar-data/src/lexer.rs
index f1dca77..d37e5fb 100644
--- a/autosar-data/src/lexer.rs
+++ b/autosar-data/src/lexer.rs
@@ -304,6 +304,17 @@ mod test {
         }
     }
 
+    #[test]
+    fn skip_byte_order_mark() {
+        let data =
+            b"\xEF\xBB\xBFcontained characters";
+        let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
+        match lexer.next() {
+            Ok((_, ArxmlEvent::ArxmlHeader(None))) => {}
+            _ => panic!("got an error instead of ArxmlHeader"),
+        }
+    }
+
     #[test]
     fn test_incomplete_data() {
         let data = b" panic!("unexpected error, {e}"),
         }
     }
+
+    #[test]
+    fn traits() {
+        // ArxmlLexerError: Debug, Error, Eq, PartialEq, Clone
+        let err = ArxmlLexerError::IncompleteData;
+        let err2 = err.clone();
+        assert_eq!(err, err2);
+        assert_eq!(format!("{err:#?}"), format!("{err2:#?}"));
+        assert_eq!(format!("{err}"), format!("{err2}"));
+
+        // ArxmlEvent: Debug
+        let event = ArxmlEvent::ArxmlHeader(None);
+        let _ = format!("{event:#?}");
+    }
 }
diff --git a/autosar-data/src/lib.rs b/autosar-data/src/lib.rs
index ea848f9..f74f844 100644
--- a/autosar-data/src/lib.rs
+++ b/autosar-data/src/lib.rs
@@ -79,6 +79,7 @@ use parking_lot::Mutex;
 use parser::*;
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
+use std::collections::HashSet;
 use std::path::PathBuf;
 use std::sync::{Arc, Weak};
 use std::{fs::File, io::Read};
@@ -114,6 +115,7 @@ pub(crate) struct WeakAutosarProject(Weak>);
 /// In addition, this top-level structure provides chaching of Autosar paths, to allow quick resolution of cross-references.
 #[derive(Debug)]
 pub(crate) struct AutosarProjectRaw {
+    root_element: Element,
     files: Vec,
     /// identifiables is a HashMap of all named elements, needed to resolve references without doing a full search.
     identifiables: FxHashMap,
@@ -168,13 +170,20 @@ pub enum AutosarDataError {
     #[error("Invalid position for an element of this kind")]
     InvalidPosition,
 
-    /// The Autosar version of the containing file was not compatible
-    #[error("Version is not compatible")]
-    VersionIncompatible,
+    /// The Autosar version of the new file did not match the version already in use
+    #[error("Version mismatch between existing {} and new {}", .version_cur, .version_new)]
+    VersionMismatch {
+        version_cur: AutosarVersion,
+        version_new: AutosarVersion,
+    },
+
+    /// The Autosar version is not compatible with the data
+    #[error("Version {} is not compatible with the element data", .version)]
+    VersionIncompatibleData { version: AutosarVersion },
 
     /// A function that only applies to identifiable elements was called on an element which is not identifiable
-    #[error("The Element is not identifiable")]
-    ElementNotIdentifiable,
+    #[error("The Element at {} is not identifiable", .xmlpath)]
+    ElementNotIdentifiable { xmlpath: String },
 
     /// An item name is required to perform this action
     #[error("An item name is required")]
@@ -227,6 +236,10 @@ pub enum AutosarDataError {
     /// The attribute value is invalid
     #[error("The given value is not valid for this attribute")]
     InvalidAttributeValue,
+
+    /// The newly loaded file diverges from the combined model on an element which is not splittable according to the metamodel
+    #[error("The new file could not be merged, because it diverges from the model on non-splittable element {}", .path)]
+    InvalidFileMerge { path: String },
 }
 
 /// An Autosar arxml file
@@ -242,16 +255,15 @@ pub struct WeakArxmlFile(Weak>);
 /// The data of an arxml file
 #[derive(Debug)]
 pub(crate) struct ArxmlFileRaw {
-    project: WeakAutosarProject,
     pub(crate) version: AutosarVersion,
+    project: WeakAutosarProject,
     pub(crate) filename: PathBuf,
-    pub(crate) root_element: Element,
     pub(crate) xml_standalone: Option, // preserve the xml standalone attribute
 }
 
 /// An arxml element
 ///
-/// This is actually a wrapper type which provides all the necessary manipulation functions. The actual element data is
+/// This is a wrapper type which provides all the necessary manipulation functions. The actual element data is
 /// held behind Arc>.
 #[derive(Debug, Clone)]
 pub struct Element(Arc>);
@@ -268,11 +280,12 @@ pub struct WeakElement(Weak>);
 /// The data of an arxml element
 #[derive(Debug)]
 pub(crate) struct ElementRaw {
-    pub(crate) parent: ElementOrFile,
+    pub(crate) parent: ElementOrProject,
     pub(crate) elemname: ElementName,
     pub(crate) elemtype: ElementType,
     pub(crate) content: SmallVec<[ElementContent; 4]>,
     pub(crate) attributes: SmallVec<[Attribute; 1]>,
+    pub(crate) file_membership: HashSet,
 }
 
 /// A single attribute of an arxml element
@@ -322,9 +335,9 @@ pub enum ContentType {
 /// This enum is used for references to the parent of each element. For all elements other than the
 /// root element, the parent is an element. The root element itself has a referenct to the ArxmlFile structure.
 #[derive(Debug, Clone)]
-pub(crate) enum ElementOrFile {
+pub(crate) enum ElementOrProject {
     Element(WeakElement),
-    File(WeakArxmlFile),
+    Project(WeakAutosarProject),
     None, // needed while constructing the data trees, otherwise there's a chicken vs. egg problem
 }
 
diff --git a/autosar-data/src/parser.rs b/autosar-data/src/parser.rs
index 123de0b..88e86a6 100644
--- a/autosar-data/src/parser.rs
+++ b/autosar-data/src/parser.rs
@@ -278,7 +278,7 @@ impl<'a> ArxmlParser<'a> {
 
                     let path = Cow::from("");
                     let autosar_root_element = self.parse_element(
-                        ElementOrFile::None,
+                        ElementOrProject::None,
                         ElementName::Autosar,
                         attributes,
                         ElementType::ROOT,
@@ -303,7 +303,7 @@ impl<'a> ArxmlParser<'a> {
 
     fn parse_element(
         &mut self,
-        parent: ElementOrFile,
+        parent: ElementOrProject,
         element_name: ElementName,
         attributes: SmallVec<[Attribute; 1]>,
         elemtype: ElementType,
@@ -316,6 +316,7 @@ impl<'a> ArxmlParser<'a> {
             attributes,
             content: SmallVec::new(),
             elemtype,
+            file_membership: HashSet::with_capacity(0),
         })));
         let mut element = wrapped_element.0.lock();
 
@@ -341,7 +342,7 @@ impl<'a> ArxmlParser<'a> {
                         let attributes = self.parse_attribute_text(sub_elemtype, attr_text)?;
                         // recursively parse the sub element and its sub sub elements
                         let sub_element = self.parse_element(
-                            ElementOrFile::Element(wrapped_element.downgrade()),
+                            ElementOrProject::Element(wrapped_element.downgrade()),
                             name,
                             attributes,
                             sub_elemtype,
@@ -840,7 +841,7 @@ mod test {
                 "Did not get the expected parser error"
             );
         } else {
-            assert!(false, "Did not get any parser error when one was expected");
+            panic!("Did not get any parser error when one was expected");
         }
 
         if optional {
@@ -854,7 +855,7 @@ mod test {
                     "Did not get the expected parser error"
                 );
             } else {
-                assert!(false, "Did not get a parser warning");
+                panic!("Did not get a parser warning");
             }
         }
     }