1use std::{
2 collections::{HashMap, VecDeque},
3 hash::Hash,
4};
5
6use crate::*;
7
8#[derive(Debug)]
9enum MergeAction {
11 MergeEqual,
12 MergeUnequal(Element),
13 AOnly,
14 BOnly(usize),
15}
16
17impl AutosarModel {
18 #[must_use]
30 pub fn new() -> AutosarModel {
31 let version = AutosarVersion::LATEST;
32 let xsi_schemalocation =
33 CharacterData::String(format!("http://autosar.org/schema/r4.0 {}", version.filename()));
34 let xmlns = CharacterData::String("http://autosar.org/schema/r4.0".to_string());
35 let xmlns_xsi = CharacterData::String("http://www.w3.org/2001/XMLSchema-instance".to_string());
36 let root_attributes = smallvec::smallvec![
37 Attribute {
38 attrname: AttributeName::xsiSchemalocation,
39 content: xsi_schemalocation
40 },
41 Attribute {
42 attrname: AttributeName::xmlns,
43 content: xmlns
44 },
45 Attribute {
46 attrname: AttributeName::xmlnsXsi,
47 content: xmlns_xsi
48 },
49 ];
50 let root_elem = ElementRaw {
51 parent: ElementOrModel::None,
52 elemname: ElementName::Autosar,
53 elemtype: ElementType::ROOT,
54 content: SmallVec::new(),
55 attributes: root_attributes,
56 file_membership: HashSet::with_capacity(0),
57 comment: None,
58 }
59 .wrap();
60 let model = AutosarModelRaw {
61 files: Vec::new(),
62 identifiables: FxIndexMap::default(),
63 reference_origins: FxHashMap::default(),
64 relative_reference_origins: FxHashMap::default(),
65 reference_bases: FxHashMap::default(),
66 root_element: root_elem.clone(),
67 }
68 .wrap();
69 root_elem.set_parent(ElementOrModel::Model(model.downgrade()));
70 model
71 }
72
73 pub fn create_file<P: AsRef<Path>>(
101 &self,
102 filename: P,
103 version: AutosarVersion,
104 ) -> Result<ArxmlFile, AutosarDataError> {
105 let mut data = self.0.write();
106
107 if data.files.iter().any(|af| af.filename() == filename.as_ref()) {
108 return Err(AutosarDataError::DuplicateFilenameError {
109 verb: "create",
110 filename: filename.as_ref().to_path_buf(),
111 });
112 }
113
114 let new_file = ArxmlFile::new(filename, version, self);
115
116 data.files.push(new_file.clone());
117
118 let _ = data.root_element.add_to_file_restricted(&new_file);
120
121 Ok(new_file)
122 }
123
124 pub fn load_buffer<P: AsRef<Path>>(
155 &self,
156 buffer: &[u8],
157 filename: P,
158 strict: bool,
159 ) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarDataError> {
160 self.load_buffer_internal(buffer, filename.as_ref().to_path_buf(), strict)
161 }
162
163 fn load_buffer_internal(
164 &self,
165 buffer: &[u8],
166 filename: PathBuf,
167 strict: bool,
168 ) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarDataError> {
169 if self.files().any(|file| file.filename() == filename) {
170 return Err(AutosarDataError::DuplicateFilenameError { verb: "load", filename });
171 }
172
173 let mut parser = ArxmlParser::new(filename.clone(), buffer, strict);
174 let root_element = parser.parse_arxml()?;
175 let version = parser.get_fileversion();
176 let arxml_file = ArxmlFileRaw {
177 version,
178 model: self.downgrade(),
179 filename: filename.clone(),
180 xml_standalone: parser.get_standalone(),
181 }
182 .wrap();
183
184 if self.0.read().files.is_empty() {
185 root_element.set_parent(ElementOrModel::Model(self.downgrade()));
186 root_element.0.write().file_membership.insert(arxml_file.downgrade());
187 self.0.write().root_element = root_element;
188 } else {
189 let result = self.merge_file_data(&root_element, arxml_file.downgrade());
190 if let Err(error) = result {
191 let _ = self.root_element().remove_from_file(&arxml_file);
192 return Err(error);
193 }
194 }
195
196 let mut data = self.0.write();
197 data.identifiables.reserve(parser.identifiables.len());
199 for (key, value) in parser.identifiables {
200 if let Some(existing_element) = data.identifiables.get(&key).and_then(WeakElement::upgrade) {
203 if let Some(new_element) = value.upgrade()
205 && existing_element.element_name() != new_element.element_name()
206 {
207 return Err(AutosarDataError::OverlappingDataError {
209 filename,
210 path: new_element.xml_path(),
211 });
212 }
213 } else {
214 data.identifiables.insert(key, value);
215 }
216 }
217
218 data.reference_origins.reserve(parser.references.len());
220 for (refpath, referring_element, base) in parser.references {
221 if let Some(base_label) = base {
222 if let Some(xref) = data.relative_reference_origins.get_mut(&refpath) {
224 xref.push((referring_element, base_label));
225 } else {
226 data.relative_reference_origins
227 .insert(refpath, vec![(referring_element, base_label)]);
228 }
229 } else {
230 if let Some(xref) = data.reference_origins.get_mut(&refpath) {
232 xref.push(referring_element);
233 } else {
234 data.reference_origins.insert(refpath, vec![referring_element]);
235 }
236 }
237 }
238
239 data.reference_bases.reserve(parser.reference_bases.len());
241 for (base_key, base_info) in parser.reference_bases {
242 if let Some(existing_base) = data.reference_bases.get_mut(&base_key) {
243 existing_base.push(base_info);
244 } else {
245 data.reference_bases.insert(base_key, vec![base_info]);
246 }
247 }
248
249 data.files.push(arxml_file.clone());
250
251 Ok((arxml_file, parser.warnings))
252 }
253
254 fn merge_file_data(&self, new_root: &Element, new_file: WeakArxmlFile) -> Result<(), AutosarDataError> {
263 let root = self.root_element();
264 let files: HashSet<WeakArxmlFile> = self.files().map(|f| f.downgrade()).collect();
265
266 Self::merge_element(&root, &files, new_root, &new_file).map_err(|e| {
267 if let AutosarDataError::ElementInsertionConflict { parent_path, .. } = &e {
269 AutosarDataError::InvalidFileMerge {
270 path: parent_path.clone(),
271 }
272 } else {
273 e
274 }
275 })?;
276
277 self.root_element().0.write().file_membership.insert(new_file);
278
279 Ok(())
280 }
281
282 fn merge_element(
283 parent_a: &Element,
284 files: &HashSet<WeakArxmlFile>,
285 parent_b: &Element,
286 new_file: &WeakArxmlFile,
287 ) -> Result<(), AutosarDataError> {
288 let mut iter_a = parent_a.sub_elements().enumerate();
289 let mut iter_b = parent_b.sub_elements();
290 let mut item_a = iter_a.next();
291 let mut item_b = iter_b.next();
292 let mut elements_a_only = Vec::<Element>::new();
293 let mut elements_b_only = Vec::<(Element, usize)>::new();
294 let mut elements_merge = Vec::<(Element, Element)>::new();
295 let min_ver_a = files
296 .iter()
297 .filter_map(|weak| weak.upgrade().map(|f| f.version()))
298 .min()
299 .unwrap_or(AutosarVersion::LATEST);
300 let min_ver_b = new_file.upgrade().map_or(AutosarVersion::LATEST, |f| f.version());
301 let version = std::cmp::min(min_ver_a, min_ver_b);
302 let splitable = parent_a.element_type().splittable_in(version);
303
304 while let (Some((pos_a, elem_a)), Some(elem_b)) = (&item_a, &item_b) {
305 let merge_action = if elem_a.element_name() == elem_b.element_name() {
306 if elem_a.is_identifiable() {
307 Self::calc_identifiables_merge(parent_a, parent_b, elem_a, elem_b, splitable)?
308 } else {
309 Self::calc_element_merge(parent_b, elem_a, elem_b)
310 }
311 } else {
312 let parent_type = parent_a.element_type();
314 let (_, indices_a) = parent_type.find_sub_element(elem_a.element_name(), u32::MAX).unwrap();
320 let (_, indices_b) = parent_type.find_sub_element(elem_b.element_name(), u32::MAX).unwrap();
321 if indices_a < indices_b {
322 MergeAction::AOnly
326 } else {
327 MergeAction::BOnly(*pos_a)
331 }
332 };
333
334 match merge_action {
335 MergeAction::MergeEqual => {
336 elements_merge.push((elem_a.clone(), elem_b.clone()));
337 item_a = iter_a.next();
338 item_b = iter_b.next();
339 }
340 MergeAction::MergeUnequal(other_b) => {
341 elements_merge.push((elem_a.clone(), other_b));
342 item_a = iter_a.next();
343 }
344 MergeAction::AOnly => {
345 elements_a_only.push(elem_a.clone());
346 item_a = iter_a.next();
347 }
348 MergeAction::BOnly(position) => {
349 if !elements_merge.iter().any(|(_, merge_b)| merge_b == elem_b) {
350 elements_b_only.push((elem_b.clone(), position));
351 }
352 item_b = iter_b.next();
353 }
354 }
355 }
356 if let Some((_, elem_a)) = item_a {
359 elements_a_only.push(elem_a);
360 for (_, elem_a) in iter_a {
361 elements_a_only.push(elem_a);
362 }
363 }
364 if let Some(elem_b) = item_b {
365 let elem_count = parent_a.0.read().content.len();
366 if !elements_merge.iter().any(|(_, merge_b)| merge_b == &elem_b) {
367 elements_b_only.push((elem_b, elem_count));
368 }
369 for elem_b in iter_b {
370 if !elements_merge.iter().any(|(_, merge_b)| merge_b == &elem_b) {
371 elements_b_only.push((elem_b, elem_count));
372 }
373 }
374 }
375
376 for element in elements_a_only {
378 let mut elem_locked = element.0.write();
380 if elem_locked.file_membership.is_empty() {
381 files.clone_into(&mut elem_locked.file_membership);
382 }
383 }
384
385 Self::import_new_items(parent_a, elements_b_only, new_file, min_ver_b)?;
388
389 Self::merge_sub_elements(parent_a, elements_merge, files, new_file, version)?;
391
392 Ok(())
393 }
394
395 fn calc_identifiables_merge(
398 parent_a: &Element,
399 parent_b: &Element,
400 elem_a: &Element,
401 elem_b: &Element,
402 splitable: bool,
403 ) -> Result<MergeAction, AutosarDataError> {
404 Ok(if elem_a.item_name() == elem_b.item_name() {
405 MergeAction::MergeEqual
408 } else {
409 if let Some(sibling) = parent_b
412 .sub_elements()
413 .find(|e| e.element_name() == elem_a.element_name() && e.item_name() == elem_a.item_name())
414 {
415 MergeAction::MergeUnequal(sibling)
417 } else {
418 if splitable {
420 MergeAction::AOnly
421 } else {
422 return Err(AutosarDataError::InvalidFileMerge {
423 path: parent_a.xml_path(),
424 });
425 }
426 }
427 })
428 }
429
430 fn calc_element_merge(parent_b: &Element, elem_a: &Element, elem_b: &Element) -> MergeAction {
433 let defref_a = elem_a
435 .get_sub_element(ElementName::DefinitionRef)
436 .and_then(|dr| dr.character_data())
437 .and_then(|cdata| cdata.string_value());
438 let defref_b = elem_b
439 .get_sub_element(ElementName::DefinitionRef)
440 .and_then(|dr| dr.character_data())
441 .and_then(|cdata| cdata.string_value());
442 if defref_a == defref_b {
445 if elem_a.character_data() != elem_b.character_data() {
447 MergeAction::AOnly
450 } else {
451 MergeAction::MergeEqual
454 }
455 } else {
456 if let Some(sibling) = parent_b
459 .sub_elements()
460 .filter(|e| e.element_name() == elem_a.element_name())
461 .find(|e| {
462 e.get_sub_element(ElementName::DefinitionRef)
463 .and_then(|dr| dr.character_data())
464 .and_then(|cdata| cdata.string_value())
465 == defref_a
466 })
467 {
468 MergeAction::MergeUnequal(sibling)
470 } else {
471 MergeAction::AOnly
474 }
475 }
476 }
477
478 fn import_new_items(
479 parent_a: &Element,
480 elements_b_only: Vec<(Element, usize)>,
481 new_file: &WeakArxmlFile,
482 version: AutosarVersion,
483 ) -> Result<(), AutosarDataError> {
484 for (idx, (new_element, insert_pos)) in elements_b_only.into_iter().enumerate() {
486 let dest = insert_pos + idx;
488
489 Self::import_single_item(parent_a, new_element, dest, new_file, version)?;
490 }
491 Ok(())
492 }
493
494 fn import_single_item(
495 parent_a: &Element,
496 new_element: Element,
497 dest: usize,
498 new_file: &WeakArxmlFile,
499 version: AutosarVersion,
500 ) -> Result<(), AutosarDataError> {
501 let mut parent_a_locked = parent_a.0.write();
502 let weak_parent_a = parent_a.downgrade();
503
504 new_element.set_parent(ElementOrModel::Element(weak_parent_a));
505 new_element.0.write().file_membership.insert(new_file.clone());
507
508 let (first_pos, last_pos) = parent_a_locked.calc_element_insert_range(new_element.element_name(), version)?;
511
512 let dest = dest.max(first_pos).min(last_pos);
514
515 parent_a_locked
517 .content
518 .insert(dest, ElementContent::Element(new_element));
519
520 Ok(())
521 }
522
523 fn merge_sub_elements(
524 parent_a: &Element,
525 elements_merge: Vec<(Element, Element)>,
526 files: &HashSet<WeakArxmlFile>,
527 new_file: &WeakArxmlFile,
528 version: AutosarVersion,
529 ) -> Result<(), AutosarDataError> {
530 for (elem_a, elem_b) in elements_merge {
531 let files = if !elem_a.0.read().file_membership.is_empty() {
533 elem_a.0.read().file_membership.clone()
534 } else {
535 files.clone()
536 };
537
538 let result = AutosarModel::merge_element(&elem_a, &files, &elem_b, new_file);
540 match result {
541 Ok(()) => {
542 let mut elem_a_locked = elem_a.0.write();
544 if !elem_a_locked.file_membership.is_empty() {
545 elem_a_locked.file_membership.insert(new_file.clone());
546 }
547 }
548 Err(e) => {
549 if let AutosarDataError::ElementInsertionConflict { parent_path, .. } = &e {
550 if elem_a.is_identifiable() {
552 return Err(AutosarDataError::InvalidFileMerge {
554 path: parent_path.clone(),
555 });
556 } else if parent_a.element_type().splittable_in(version) {
557 elem_a.set_file_membership(files);
558 let dest = elem_a.position().unwrap_or_default() + 1;
560 return Self::import_single_item(parent_a, elem_b, dest, new_file, version).map_err(|_| e);
561 }
562 }
563
564 return Err(e);
566 }
567 }
568 }
569 Ok(())
570 }
571
572 pub fn load_file<P: AsRef<Path>>(
600 &self,
601 filename: P,
602 strict: bool,
603 ) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarDataError> {
604 let filename_buf = filename.as_ref().to_path_buf();
605 let buffer = std::fs::read(&filename_buf).map_err(|err| AutosarDataError::IoErrorRead {
606 filename: filename_buf.clone(),
607 ioerror: err,
608 })?;
609
610 self.load_buffer(&buffer, &filename_buf, strict)
611 }
612
613 pub fn remove_file(&self, file: &ArxmlFile) {
631 let mut locked_model = self.0.write();
632 let find_result = locked_model
633 .files
634 .iter()
635 .enumerate()
636 .find(|(_, f)| *f == file)
637 .map(|(pos, _)| pos);
638 if let Some(pos) = find_result {
640 locked_model.files.swap_remove(pos);
641 if locked_model.files.is_empty() {
642 locked_model.root_element.0.write().content.clear();
644 locked_model.root_element.set_file_membership(HashSet::new());
645 locked_model.identifiables.clear();
646 locked_model.reference_origins.clear();
647 locked_model.relative_reference_origins.clear();
648 locked_model.reference_bases.clear();
649 } else {
650 drop(locked_model);
651 let _ = self.root_element().remove_from_file(file);
653 }
654 }
655 }
656
657 #[must_use]
675 pub fn serialize_files(&self) -> HashMap<PathBuf, String> {
676 let mut result = HashMap::new();
677 for file in self.files() {
678 if let Ok(data) = file.serialize() {
679 result.insert(file.filename(), data);
680 }
681 }
682 result
683 }
684
685 pub fn write(&self) -> Result<(), AutosarDataError> {
708 for (pathbuf, filedata) in self.serialize_files() {
709 std::fs::write(pathbuf.clone(), filedata).map_err(|err| AutosarDataError::IoErrorWrite {
710 filename: pathbuf,
711 ioerror: err,
712 })?;
713 }
714 Ok(())
715 }
716
717 #[must_use]
733 pub fn files(&self) -> ArxmlFileIterator {
734 ArxmlFileIterator::new(self.clone())
735 }
736
737 #[must_use]
748 pub fn root_element(&self) -> Element {
749 let locked_model = self.0.read();
750 locked_model.root_element.clone()
751 }
752
753 #[must_use]
775 pub fn get_element_by_path(&self, path: &str) -> Option<Element> {
776 let model = self.0.read();
777 model.identifiables.get(path).and_then(WeakElement::upgrade)
778 }
779
780 pub fn duplicate(&self) -> Result<AutosarModel, AutosarDataError> {
807 let copy = Self::new();
808 let mut filemap = HashMap::new();
809
810 for orig_file in self.files() {
811 let filename = orig_file.filename();
812 let new_file = copy.create_file(filename.clone(), orig_file.version())?;
813 new_file.0.write().xml_standalone = orig_file.0.read().xml_standalone;
814 filemap.insert(filename, new_file.downgrade());
815 }
816
817 for element in self.root_element().sub_elements() {
820 copy.root_element().create_copied_sub_element(&element)?;
821 }
822
823 let orig_iter = self.elements_dfs();
826 let copy_iter = copy.elements_dfs();
827 let combined = std::iter::zip(orig_iter, copy_iter);
828 for ((_, orig_elem), (_, copy_elem)) in combined {
829 let mut locked_copy = copy_elem.0.try_write().ok_or(AutosarDataError::ParentElementLocked)?;
830 locked_copy.file_membership.clear();
831
832 for orig_file in orig_elem.0.read().file_membership.iter().filter_map(|w| w.upgrade()) {
833 if let Some(copy_file) = filemap.get(&orig_file.filename()) {
834 locked_copy.file_membership.insert(copy_file.clone());
835 }
836 }
837 }
838
839 Ok(copy)
840 }
841
842 #[must_use]
870 pub fn elements_dfs(&self) -> ElementsDfsIterator {
871 self.root_element().elements_dfs()
872 }
873
874 #[must_use]
895 pub fn elements_dfs_with_max_depth(&self, max_depth: usize) -> ElementsDfsIterator {
896 self.root_element().elements_dfs_with_max_depth(max_depth)
897 }
898
899 pub fn sort(&self) {
916 self.root_element().sort();
917 }
918
919 #[must_use]
937 pub fn identifiable_elements(&self) -> IdentifiablesIterator {
938 IdentifiablesIterator::new(self)
939 }
940
941 #[must_use]
964 pub fn get_references_to(&self, target_path: &str) -> Vec<WeakElement> {
965 let locked_model = self.0.read();
966 let mut origins = if let Some(origins) = locked_model.reference_origins.get(target_path) {
967 origins.clone()
968 } else {
969 Vec::new()
970 };
971
972 if !locked_model.relative_reference_origins.is_empty() {
973 let mut pos = target_path.rfind('/').map(|i| i + 1).unwrap_or(0);
975 while pos > 0 {
976 let suffix = &target_path[pos..];
977 if let Some(rel_origins) = locked_model.relative_reference_origins.get(suffix) {
978 for (origin, base_label) in rel_origins {
979 let Some(origin_elem) = origin.upgrade() else {
980 continue;
981 };
982 let Some(package_path) = origin_elem.package().ok().flatten().and_then(|p| p.path().ok())
983 else {
984 continue;
985 };
986 let Some(base_path) = self.resolve_reference_base(base_label, &package_path) else {
987 continue;
988 };
989 if let Some(rest) = target_path.strip_prefix(&base_path) {
990 let rest = rest.strip_prefix('/').unwrap_or(rest);
991 if rest == suffix {
992 origins.push(origin.clone());
993 }
994 }
995 }
996 }
997 pos = target_path[..pos - 1].rfind('/').map(|i| i + 1).unwrap_or(0);
998 }
999 }
1000
1001 origins
1002 }
1003
1004 #[must_use]
1024 pub fn check_references(&self) -> Vec<WeakElement> {
1025 let mut broken_refs = Vec::new();
1026
1027 let model = self.0.read();
1028 for (path, element_list) in &model.reference_origins {
1029 if let Some(target_elem_weak) = model.identifiables.get(path) {
1030 if let Some(target_elem) = target_elem_weak.upgrade() {
1032 for referring_elem_weak in element_list {
1035 if let Some(referring_elem) = referring_elem_weak.upgrade() {
1036 if let Some(CharacterData::Enum(dest_value)) =
1037 referring_elem.attribute_value(AttributeName::Dest)
1038 {
1039 if !target_elem.element_type().verify_reference_dest(dest_value) {
1040 broken_refs.push(referring_elem_weak.clone());
1042 }
1043 } else {
1044 broken_refs.push(referring_elem_weak.clone());
1046 }
1047 }
1048 }
1049 } else {
1050 broken_refs.extend(element_list.iter().cloned());
1054 }
1055 } else {
1056 broken_refs.extend(element_list.iter().cloned());
1058 }
1059 }
1060
1061 broken_refs
1062 }
1063
1064 pub(crate) fn downgrade(&self) -> WeakAutosarModel {
1066 WeakAutosarModel(Arc::downgrade(&self.0))
1067 }
1068
1069 pub(crate) fn add_identifiable(&self, new_path: String, elem: WeakElement) {
1071 let mut model = self.0.write();
1072 model.identifiables.insert(new_path, elem);
1073 }
1074
1075 pub(crate) fn fix_identifiables(&self, old_path: &str, new_path: &str) {
1077 let mut model = self.0.write();
1078
1079 let keys: Vec<String> = model.identifiables.keys().cloned().collect();
1081 for key in keys {
1082 if let Some(suffix) = key.strip_prefix(old_path)
1084 && (suffix.is_empty() || suffix.starts_with('/'))
1085 {
1086 let new_key = format!("{new_path}{suffix}");
1087 if let Some(entry) = model.identifiables.swap_remove(&key) {
1089 model.identifiables.insert(new_key, entry);
1090 }
1091 }
1092 }
1093 }
1094
1095 pub(crate) fn remove_identifiable(&self, path: &str) {
1097 let mut model = self.0.write();
1098 model.identifiables.swap_remove(path);
1099 }
1100
1101 pub(crate) fn add_reference_origin(&self, new_ref: &str, base: Option<&str>, origin: WeakElement) {
1102 if let Some(base) = base {
1103 self.add_relative_reference_origin(new_ref, base, origin);
1104 } else {
1105 self.add_absolute_reference_origin(new_ref, origin);
1106 }
1107 }
1108
1109 fn add_absolute_reference_origin(&self, new_ref: &str, origin: WeakElement) {
1110 let mut data = self.0.write();
1111 if let Some(referrer_list) = data.reference_origins.get_mut(new_ref) {
1113 referrer_list.push(origin);
1114 } else {
1115 data.reference_origins.insert(new_ref.to_owned(), vec![origin]);
1116 }
1117 }
1118
1119 fn add_relative_reference_origin(&self, new_ref: &str, base: &str, origin: WeakElement) {
1120 let mut data = self.0.write();
1121 if let Some(referrer_list) = data.relative_reference_origins.get_mut(new_ref) {
1122 referrer_list.push((origin, base.to_string()));
1123 } else {
1124 data.relative_reference_origins
1125 .insert(new_ref.to_owned(), vec![(origin, base.to_string())]);
1126 }
1127 }
1128
1129 pub(crate) fn fix_reference_origins(
1130 &self,
1131 old_ref: &str,
1132 new_ref: &str,
1133 old_base: Option<&str>,
1134 new_base: Option<&str>,
1135 origin: WeakElement,
1136 ) {
1137 if old_ref != new_ref || old_base != new_base {
1138 self.remove_reference_origin(old_ref, old_base, origin.clone());
1139 self.add_reference_origin(new_ref, new_base, origin);
1140 }
1141 }
1142
1143 pub(crate) fn remove_reference_origin(&self, reference: &str, base: Option<&str>, element: WeakElement) {
1144 if let Some(base) = base {
1145 self.remove_relative_reference_origin(reference, base, element);
1146 } else {
1147 self.remove_absolute_reference_origin(reference, element);
1148 }
1149 }
1150
1151 fn remove_absolute_reference_origin(&self, reference: &str, element: WeakElement) {
1152 let mut data = self.0.write();
1153 let mut count = 1;
1154 if let Some(referrer_list) = data.reference_origins.get_mut(reference) {
1155 if let Some(index) = referrer_list.iter().position(|x| *x == element) {
1156 referrer_list.swap_remove(index);
1157 }
1158 count = referrer_list.len();
1159 }
1160 if count == 0 {
1161 data.reference_origins.remove(reference);
1162 }
1163 }
1164
1165 fn remove_relative_reference_origin(&self, reference: &str, base: &str, element: WeakElement) {
1166 let mut data = self.0.write();
1167 let mut count = 1;
1168 if let Some(referrer_list) = data.relative_reference_origins.get_mut(reference) {
1169 if let Some(index) = referrer_list.iter().position(|(x, b)| *x == element && *b == base) {
1170 referrer_list.swap_remove(index);
1171 }
1172 count = referrer_list.len();
1173 }
1174 if count == 0 {
1175 data.relative_reference_origins.remove(reference);
1176 }
1177 }
1178
1179 pub(crate) fn resolve_reference_base(&self, base: &str, ref_package_path: &str) -> Option<String> {
1180 let model = self.0.read();
1181 let mut current_base = base;
1182 let mut path_components = VecDeque::with_capacity(0);
1183 let mut ref_package_path = ref_package_path;
1184 loop {
1185 let ref_base_info_list = model.reference_bases.get(current_base)?;
1187 let ref_base_info = ref_base_info_list
1189 .iter()
1190 .filter(|info| ref_package_path.starts_with(&info.owner_package_path))
1191 .max_by_key(|info| info.owner_package_path.len())?;
1192
1193 if let Some(package_ref_base) = &ref_base_info.package_ref_base {
1195 path_components.push_front(&ref_base_info.package_ref);
1198 current_base = package_ref_base;
1199 ref_package_path = &ref_base_info.owner_package_path;
1200 } else {
1201 if path_components.is_empty() {
1202 return Some(ref_base_info.package_ref.clone());
1203 } else {
1204 path_components.push_front(&ref_base_info.package_ref);
1205 let mut resolved_path = String::new();
1207 for component in path_components {
1208 resolved_path.push_str(component);
1209 resolved_path.push('/');
1210 }
1211 resolved_path.pop(); return Some(resolved_path);
1213 }
1214 }
1215 }
1216 }
1217
1218 pub(crate) fn fix_reference_base(
1219 &self,
1220 old_label: Option<String>,
1221 new_label: String,
1222 package_ref_val: String,
1223 ref_base_attr: Option<String>,
1224 owner_package_path: String,
1225 ) {
1226 let mut data = self.0.write();
1227 if let Some(old_label) = old_label {
1229 let mut remove_old_key = false;
1230 if let Some(ref_base_info_list) = data.reference_bases.get_mut(&old_label) {
1231 if let Some(index) = ref_base_info_list
1232 .iter()
1233 .position(|info| info.owner_package_path == owner_package_path)
1234 {
1235 ref_base_info_list.swap_remove(index);
1236 }
1237 remove_old_key = ref_base_info_list.is_empty();
1238 }
1239 if remove_old_key {
1240 data.reference_bases.remove(&old_label);
1241 }
1242 }
1243
1244 let ref_base_info_list = data.reference_bases.entry(new_label).or_default();
1246 if let Some(existing_info) = ref_base_info_list
1247 .iter_mut()
1248 .find(|info| info.owner_package_path == owner_package_path)
1249 {
1250 existing_info.package_ref = package_ref_val;
1251 existing_info.package_ref_base = ref_base_attr;
1252 } else {
1253 ref_base_info_list.push(ReferenceBaseInfo {
1254 package_ref: package_ref_val,
1255 package_ref_base: ref_base_attr,
1256 owner_package_path,
1257 });
1258 }
1259 }
1260
1261 pub(crate) fn remove_reference_base(&self, label: &str, owner_package_path: &str) {
1263 let mut data = self.0.write();
1264 if let Some(ref_base_info_list) = data.reference_bases.get_mut(label)
1265 && let Some(index) = ref_base_info_list
1266 .iter()
1267 .position(|info| info.owner_package_path == owner_package_path)
1268 {
1269 ref_base_info_list.swap_remove(index);
1270 if ref_base_info_list.is_empty() {
1271 data.reference_bases.remove(label);
1272 }
1273 }
1274 }
1275}
1276
1277impl AutosarModelRaw {
1278 pub(crate) fn set_version(&mut self, new_ver: AutosarVersion) {
1279 let attribute_value = CharacterData::String(format!("http://autosar.org/schema/r4.0 {}", new_ver.filename()));
1280 let _ = self.root_element.0.write().set_attribute_internal(
1281 AttributeName::xsiSchemalocation,
1282 attribute_value,
1283 new_ver,
1284 );
1285 }
1286
1287 pub(crate) fn wrap(self) -> AutosarModel {
1288 AutosarModel(Arc::new(RwLock::new(self)))
1289 }
1290}
1291
1292impl std::fmt::Debug for AutosarModel {
1293 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1294 let model = self.0.read();
1295 let rootelem = model.root_element.clone();
1298 let mut dbgstruct = f.debug_struct("AutosarModel");
1299 dbgstruct.field("root_element", &rootelem);
1300 dbgstruct.field("files", &model.files);
1301 dbgstruct.field("identifiables", &model.identifiables);
1302 dbgstruct.field("reference_origins", &model.reference_origins);
1303 dbgstruct.field("relative_reference_origins", &model.relative_reference_origins);
1304 dbgstruct.field("reference_bases", &model.reference_bases);
1305 dbgstruct.finish()
1306 }
1307}
1308
1309impl Default for AutosarModel {
1310 fn default() -> Self {
1311 Self::new()
1312 }
1313}
1314
1315impl PartialEq for AutosarModel {
1316 fn eq(&self, other: &Self) -> bool {
1317 Arc::as_ptr(&self.0) == Arc::as_ptr(&other.0)
1318 }
1319}
1320
1321impl Eq for AutosarModel {}
1322
1323impl Hash for AutosarModel {
1324 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1325 state.write_usize(Arc::as_ptr(&self.0) as usize);
1326 }
1327}
1328
1329impl WeakAutosarModel {
1330 pub(crate) fn upgrade(&self) -> Option<AutosarModel> {
1331 Weak::upgrade(&self.0).map(AutosarModel)
1332 }
1333}
1334
1335impl std::fmt::Debug for WeakAutosarModel {
1336 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1337 f.write_fmt(format_args!("AutosarModel:WeakRef {:p}", Weak::as_ptr(&self.0)))
1338 }
1339}
1340
1341#[cfg(test)]
1342mod test {
1343 use super::*;
1344 use tempfile::tempdir;
1345
1346 #[test]
1347 fn create_file() {
1348 let model = AutosarModel::new();
1349 let file = model.create_file("test", AutosarVersion::Autosar_00050);
1350 assert!(file.is_ok());
1351 let file = model.create_file("test", AutosarVersion::Autosar_00050);
1353 assert!(file.is_err());
1354 }
1355
1356 #[test]
1357 fn load_buffer() {
1358 const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1359 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1360 <AR-PACKAGES>
1361 <AR-PACKAGE>
1362 <SHORT-NAME>Pkg</SHORT-NAME>
1363 <ELEMENTS>
1364 <SYSTEM><SHORT-NAME>Thing</SHORT-NAME></SYSTEM>
1365 </ELEMENTS>
1366 </AR-PACKAGE>
1367 </AR-PACKAGES></AUTOSAR>"#;
1368 const FILEBUF2: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1369 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1370 <AR-PACKAGES>
1371 <AR-PACKAGE><SHORT-NAME>OtherPkg</SHORT-NAME></AR-PACKAGE>
1372 </AR-PACKAGES></AUTOSAR>"#;
1373 const FILEBUF3: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1374 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1375 <AR-PACKAGES>
1376 <AR-PACKAGE>
1377 <SHORT-NAME>Pkg</SHORT-NAME>
1378 <ELEMENTS>
1379 <APPLICATION-PRIMITIVE-DATA-TYPE><SHORT-NAME>Thing</SHORT-NAME></APPLICATION-PRIMITIVE-DATA-TYPE>
1380 </ELEMENTS>
1381 </AR-PACKAGE>
1382 </AR-PACKAGES></AUTOSAR>"#;
1383 const NON_ARXML: &str = "The quick brown fox jumps over the lazy dog";
1384 let model = AutosarModel::new();
1385 let result = model.load_buffer(FILEBUF.as_bytes(), "test", true);
1387 assert!(result.is_ok());
1388 let result = model.load_buffer(FILEBUF2.as_bytes(), "other", true);
1390 assert!(result.is_ok());
1391 let result = model.load_buffer(FILEBUF.as_bytes(), "test", true);
1393 assert!(result.is_err());
1394 let result = model.load_buffer(FILEBUF3.as_bytes(), "test2", true);
1396 assert!(result.is_err());
1397 let result = model.load_buffer(NON_ARXML.as_bytes(), "nonsense", true);
1399 assert!(result.is_err());
1400 }
1401
1402 #[test]
1403 fn load_file() {
1404 let dir = tempdir().unwrap();
1405
1406 let model = AutosarModel::new();
1407 let filename = dir.path().with_file_name("nonexistent.arxml");
1408 assert!(model.load_file(&filename, true).is_err());
1409
1410 let filename = dir.path().with_file_name("test.arxml");
1411 model.create_file(&filename, AutosarVersion::LATEST).unwrap();
1412 model
1413 .root_element()
1414 .create_sub_element(ElementName::ArPackages)
1415 .and_then(|ap| ap.create_named_sub_element(ElementName::ArPackage, "Pkg"))
1416 .unwrap();
1417 model.write().unwrap();
1418
1419 assert!(filename.exists());
1420
1421 let model = AutosarModel::new();
1423 model.load_file(&filename, true).unwrap();
1424 let el_pkg = model.get_element_by_path("/Pkg");
1425 assert!(el_pkg.is_some());
1426 }
1427
1428 #[test]
1429 fn data_merge() {
1430 const FILEBUF1: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1431 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1432 <AR-PACKAGES>
1433 <AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
1434 <ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
1435 <SHORT-NAME>BswModuleValues</SHORT-NAME>
1436 <PARAMETER-VALUES>
1437 <ECUC-NUMERICAL-PARAM-VALUE>
1438 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
1439 </ECUC-NUMERICAL-PARAM-VALUE>
1440 <ECUC-NUMERICAL-PARAM-VALUE>
1441 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_B</DEFINITION-REF>
1442 </ECUC-NUMERICAL-PARAM-VALUE>
1443 <ECUC-NUMERICAL-PARAM-VALUE>
1444 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_C</DEFINITION-REF>
1445 </ECUC-NUMERICAL-PARAM-VALUE>
1446 </PARAMETER-VALUES>
1447 </ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
1448 </ELEMENTS></AR-PACKAGE>
1449 <AR-PACKAGE><SHORT-NAME>Pkg_B</SHORT-NAME></AR-PACKAGE>
1450 <AR-PACKAGE><SHORT-NAME>Pkg_C</SHORT-NAME></AR-PACKAGE>
1451 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
1452 const FILEBUF2: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1453 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1454 <AR-PACKAGES>
1455 <AR-PACKAGE><SHORT-NAME>Pkg_B</SHORT-NAME></AR-PACKAGE>
1456 <AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
1457 <ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
1458 <SHORT-NAME>BswModuleValues</SHORT-NAME>
1459 <PARAMETER-VALUES>
1460 <ECUC-NUMERICAL-PARAM-VALUE>
1461 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_B</DEFINITION-REF>
1462 </ECUC-NUMERICAL-PARAM-VALUE>
1463 <ECUC-NUMERICAL-PARAM-VALUE>
1464 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
1465 </ECUC-NUMERICAL-PARAM-VALUE>
1466 </PARAMETER-VALUES>
1467 </ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
1468 </ELEMENTS></AR-PACKAGE>
1469 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
1470 let model = AutosarModel::new();
1473 let (file1, _) = model.load_buffer(FILEBUF1, "test1", true).unwrap();
1474 let file1_elemcount = file1.elements_dfs().count();
1475 let (file2, _) = model.load_buffer(FILEBUF2, "test2", true).unwrap();
1476 let file2_elemcount = file2.elements_dfs().count();
1477 let model_elemcount = model.elements_dfs().count();
1478 assert_eq!(file1_elemcount, model_elemcount);
1479 assert!(file1_elemcount > file2_elemcount);
1480 let (local, fileset) = model.root_element().file_membership().unwrap();
1482 assert!(local);
1483 assert_eq!(fileset.len(), 2);
1484
1485 let el_pkg_c = model.get_element_by_path("/Pkg_C").unwrap();
1486 let (local, fileset) = el_pkg_c.file_membership().unwrap();
1487 assert!(local);
1488 assert_eq!(fileset.len(), 1);
1489 let el_npv2 = model
1490 .get_element_by_path("/Pkg_A/BswModule/BswModuleValues")
1491 .and_then(|bmv| bmv.get_sub_element(ElementName::ParameterValues))
1492 .and_then(|pv| pv.get_sub_element_at(2))
1493 .unwrap();
1494 let (loc, fm) = el_npv2.file_membership().unwrap();
1495 assert!(loc);
1496 assert_eq!(fm.len(), 1);
1497
1498 const ERRFILE1: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1501 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1502 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
1503 <ELEMENTS>
1504 <SYSTEM-TIMING>
1505 <SHORT-NAME>SystemTimings</SHORT-NAME>
1506 <CATEGORY>CAT</CATEGORY>
1507 <TIMING-RESOURCE>
1508 <SHORT-NAME>Name_One</SHORT-NAME>
1509 </TIMING-RESOURCE>
1510 </SYSTEM-TIMING>
1511 </ELEMENTS>
1512 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
1513 const ERRFILE2: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1514 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1515 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
1516 <ELEMENTS>
1517 <SYSTEM-TIMING>
1518 <SHORT-NAME>SystemTimings</SHORT-NAME>
1519 <TIMING-RESOURCE>
1520 <SHORT-NAME>Name_Two</SHORT-NAME>
1521 </TIMING-RESOURCE>
1522 </SYSTEM-TIMING>
1523 </ELEMENTS>
1524 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
1525 let model = AutosarModel::new();
1526 let result = model.load_buffer(ERRFILE1, "test1", true);
1527 assert!(result.is_ok());
1528 let result = model.load_buffer(ERRFILE2, "test2", true);
1529 let error = result.unwrap_err();
1530 assert!(matches!(error, AutosarDataError::InvalidFileMerge { .. }));
1531
1532 const ERRFILE3: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1536 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1537 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
1538 <ELEMENTS>
1539 <COMPU-METHOD><SHORT-NAME>compu</SHORT-NAME>
1540 <COMPU-INTERNAL-TO-PHYS>
1541 <COMPU-SCALES>
1542 <COMPU-SCALE><COMPU-CONST></COMPU-CONST></COMPU-SCALE>
1543 </COMPU-SCALES>
1544 </COMPU-INTERNAL-TO-PHYS>
1545 </COMPU-METHOD>
1546 </ELEMENTS>
1547 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
1548 const ERRFILE4: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1549 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1550 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
1551 <ELEMENTS>
1552 <COMPU-METHOD><SHORT-NAME>compu</SHORT-NAME>
1553 <COMPU-INTERNAL-TO-PHYS>
1554 <COMPU-SCALES>
1555 <COMPU-SCALE><COMPU-RATIONAL-COEFFS></COMPU-RATIONAL-COEFFS></COMPU-SCALE>
1556 </COMPU-SCALES>
1557 </COMPU-INTERNAL-TO-PHYS>
1558 </COMPU-METHOD>
1559 </ELEMENTS>
1560 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
1561 let model = AutosarModel::new();
1562 let result = model.load_buffer(ERRFILE3, "test3", true);
1563 assert!(result.is_ok());
1564 let result = model.load_buffer(ERRFILE4, "test4", true);
1565 let error = result.unwrap_err();
1566 assert!(matches!(error, AutosarDataError::InvalidFileMerge { .. }));
1567
1568 const FILEBUF3: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1570 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1571 <AR-PACKAGES>
1572 <AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME></AR-PACKAGE>
1573 <AR-PACKAGE><SHORT-NAME>Package2</SHORT-NAME></AR-PACKAGE>
1574 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
1575 const FILEBUF4: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1576 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1577 <AR-PACKAGES>
1578 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
1579 let model_a = AutosarModel::new();
1580 model_a.load_buffer(FILEBUF3, "test5", true).unwrap();
1581 model_a.load_buffer(FILEBUF4, "test6", true).unwrap();
1582 let model_b = AutosarModel::new();
1584 model_b.load_buffer(FILEBUF4, "test5", true).unwrap();
1585 model_b.load_buffer(FILEBUF3, "test6", true).unwrap();
1586 model_a.sort();
1588 let model_a_txt = model_a.root_element().serialize();
1589 model_b.sort();
1590 let model_b_txt = model_b.root_element().serialize();
1591 assert_eq!(model_a_txt, model_b_txt);
1592 }
1593
1594 #[test]
1595 fn remove_file() {
1596 const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1597 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1598 <AR-PACKAGES>
1599 <AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME></AR-PACKAGE>
1600 </AR-PACKAGES></AUTOSAR>"#;
1601 const FILEBUF2: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1602 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00049.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1603 <AR-PACKAGES>
1604 <AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
1605 <ELEMENTS><CAN-CLUSTER><SHORT-NAME>CAN_Cluster</SHORT-NAME></CAN-CLUSTER></ELEMENTS>
1606 </AR-PACKAGE>
1607 </AR-PACKAGES></AUTOSAR>"#;
1608 const FILEBUF3: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1609 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00048.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1610 <AR-PACKAGES>
1611 <AR-PACKAGE><SHORT-NAME>Package2</SHORT-NAME>
1612 <ELEMENTS><SYSTEM><SHORT-NAME>System</SHORT-NAME>
1613 <FIBEX-ELEMENTS><FIBEX-ELEMENT-REF-CONDITIONAL>
1614 <FIBEX-ELEMENT-REF DEST="CAN-CLUSTER">/Package/CAN_Cluster</FIBEX-ELEMENT-REF>
1615 </FIBEX-ELEMENT-REF-CONDITIONAL></FIBEX-ELEMENTS>
1616 </SYSTEM></ELEMENTS></AR-PACKAGE>
1617 </AR-PACKAGES></AUTOSAR>"#;
1618 let model = AutosarModel::new();
1620 let (file, _) = model.load_buffer(FILEBUF.as_bytes(), "test", true).unwrap();
1621 assert_eq!(model.files().count(), 1);
1622 assert_eq!(model.identifiable_elements().count(), 1);
1623 model.remove_file(&file);
1624 assert_eq!(model.files().count(), 0);
1625 assert_eq!(model.identifiable_elements().count(), 0);
1626 let model = AutosarModel::new();
1628 model.load_buffer(FILEBUF.as_bytes(), "test1", true).unwrap();
1629 assert_eq!(model.files().count(), 1);
1630 let modeltxt_1 = model.root_element().serialize();
1631 let (file2, _) = model.load_buffer(FILEBUF2.as_bytes(), "test2", true).unwrap();
1632 assert_eq!(model.files().count(), 2);
1633 let modeltxt_1_2 = model.root_element().serialize();
1634 assert_ne!(modeltxt_1, modeltxt_1_2);
1635 let (file3, _) = model.load_buffer(FILEBUF3.as_bytes(), "test3", true).unwrap();
1636 assert_eq!(model.files().count(), 3);
1637 let modeltxt_1_2_3 = model.root_element().serialize();
1638 assert_ne!(modeltxt_1_2, modeltxt_1_2_3);
1639 model.get_element_by_path("/Package2/System").unwrap();
1640 model.remove_file(&file3);
1641 let modeltxt_1_2_x = model.root_element().serialize();
1643 assert_eq!(modeltxt_1_2, modeltxt_1_2_x);
1644 model.remove_file(&file2);
1645 let modeltxt_1_x_x = model.root_element().serialize();
1647 assert_eq!(modeltxt_1, modeltxt_1_x_x);
1648 assert_eq!(model.files().count(), 1);
1649 }
1650
1651 #[test]
1652 fn remove_last_file_clears_relative_reference_origins() {
1653 const FILEBUF: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1654<AUTOSAR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://autosar.org/schema/r4.0" xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd">
1655 <AR-PACKAGES>
1656 <AR-PACKAGE>
1657 <SHORT-NAME>BasePkg</SHORT-NAME>
1658 <ELEMENTS>
1659 <ECU-INSTANCE>
1660 <SHORT-NAME>Ecu</SHORT-NAME>
1661 </ECU-INSTANCE>
1662 </ELEMENTS>
1663 </AR-PACKAGE>
1664 <AR-PACKAGE>
1665 <SHORT-NAME>RefPkg</SHORT-NAME>
1666 <REFERENCE-BASES>
1667 <REFERENCE-BASE>
1668 <SHORT-LABEL>BaseA</SHORT-LABEL>
1669 <PACKAGE-REF DEST="AR-PACKAGE">/BasePkg</PACKAGE-REF>
1670 </REFERENCE-BASE>
1671 </REFERENCE-BASES>
1672 <ELEMENTS>
1673 <SYSTEM>
1674 <SHORT-NAME>Sys</SHORT-NAME>
1675 <FIBEX-ELEMENTS>
1676 <FIBEX-ELEMENT-REF-CONDITIONAL>
1677 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE" BASE="BaseA">Ecu</FIBEX-ELEMENT-REF>
1678 </FIBEX-ELEMENT-REF-CONDITIONAL>
1679 </FIBEX-ELEMENTS>
1680 </SYSTEM>
1681 </ELEMENTS>
1682 </AR-PACKAGE>
1683 </AR-PACKAGES>
1684</AUTOSAR>"#
1685 .as_bytes();
1686
1687 let model = AutosarModel::new();
1688 let (file, _) = model.load_buffer(FILEBUF, "test", true).unwrap();
1689
1690 assert!(!model.0.read().relative_reference_origins.is_empty());
1691 model.remove_file(&file);
1692
1693 assert!(model.0.read().relative_reference_origins.is_empty());
1694 }
1695
1696 #[test]
1697 fn refcount() {
1698 let model = AutosarModel::default();
1699 let weak = model.downgrade();
1700 let project2 = weak.upgrade();
1701 assert_eq!(Arc::strong_count(&model.0), 2);
1702 assert_eq!(model, project2.unwrap());
1703 }
1704
1705 #[test]
1706 fn identifiables_iterator() {
1707 const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1708 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1709 <AR-PACKAGES>
1710 <AR-PACKAGE><SHORT-NAME>OuterPackage1</SHORT-NAME>
1711 <AR-PACKAGES>
1712 <AR-PACKAGE><SHORT-NAME>InnerPackage1</SHORT-NAME></AR-PACKAGE>
1713 <AR-PACKAGE><SHORT-NAME>InnerPackage2</SHORT-NAME></AR-PACKAGE>
1714 </AR-PACKAGES>
1715 </AR-PACKAGE>
1716 <AR-PACKAGE><SHORT-NAME>OuterPackage2</SHORT-NAME>
1717 <AR-PACKAGES>
1718 <AR-PACKAGE><SHORT-NAME>InnerPackage1</SHORT-NAME></AR-PACKAGE>
1719 <AR-PACKAGE><SHORT-NAME>InnerPackage2</SHORT-NAME></AR-PACKAGE>
1720 </AR-PACKAGES>
1721 </AR-PACKAGE>
1722 </AR-PACKAGES></AUTOSAR>"#;
1723 let model = AutosarModel::new();
1724 model.load_buffer(FILEBUF.as_bytes(), "test", true).unwrap();
1725 let mut identifiable_elements = model.identifiable_elements().collect::<Vec<_>>();
1726 identifiable_elements.sort_by(|a, b| a.0.cmp(&b.0));
1727 assert_eq!(identifiable_elements[0].0, "/OuterPackage1");
1728 assert_eq!(identifiable_elements[1].0, "/OuterPackage1/InnerPackage1");
1729 assert_eq!(identifiable_elements[2].0, "/OuterPackage1/InnerPackage2");
1730 assert_eq!(identifiable_elements[3].0, "/OuterPackage2");
1731 assert_eq!(identifiable_elements[4].0, "/OuterPackage2/InnerPackage1");
1732 assert_eq!(identifiable_elements[5].0, "/OuterPackage2/InnerPackage2");
1733 }
1734
1735 #[test]
1736 fn check_references() {
1737 const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1738 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1739 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Pkg</SHORT-NAME>
1740 <ELEMENTS>
1741 <SYSTEM><SHORT-NAME>System</SHORT-NAME>
1742 <FIBEX-ELEMENTS>
1743 <FIBEX-ELEMENT-REF-CONDITIONAL>
1744 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE">/Pkg/EcuInstance</FIBEX-ELEMENT-REF>
1745 </FIBEX-ELEMENT-REF-CONDITIONAL>
1746 <FIBEX-ELEMENT-REF-CONDITIONAL>
1747 <FIBEX-ELEMENT-REF DEST="I-SIGNAL-I-PDU">/Some/Invalid/Path</FIBEX-ELEMENT-REF>
1748 </FIBEX-ELEMENT-REF-CONDITIONAL>
1749 <FIBEX-ELEMENT-REF-CONDITIONAL>
1750 <FIBEX-ELEMENT-REF DEST="I-SIGNAL">/Pkg/System</FIBEX-ELEMENT-REF>
1751 </FIBEX-ELEMENT-REF-CONDITIONAL>
1752 </FIBEX-ELEMENTS>
1753 </SYSTEM>
1754 <ECU-INSTANCE><SHORT-NAME>EcuInstance</SHORT-NAME></ECU-INSTANCE>
1755 </ELEMENTS>
1756 </AR-PACKAGE>
1757 </AR-PACKAGES></AUTOSAR>"#;
1758 let model = AutosarModel::new();
1759 model.load_buffer(FILEBUF.as_bytes(), "test", true).unwrap();
1760 let el_system = model.get_element_by_path("/Pkg/System").unwrap();
1761 let el_fibex_elements = el_system.get_sub_element(ElementName::FibexElements).unwrap();
1762 let el_fibex_element_ref = el_fibex_elements
1763 .create_sub_element(ElementName::FibexElementRefConditional)
1764 .and_then(|ferc| ferc.create_sub_element(ElementName::FibexElementRef))
1765 .unwrap();
1766 el_fibex_element_ref.set_character_data("/Pkg/System").unwrap();
1767 assert_eq!(model.0.read().reference_origins.len(), 3);
1773
1774 let el_fbx_ref1 = el_fibex_elements
1776 .get_sub_element_at(0)
1777 .and_then(|ferc| ferc.get_sub_element(ElementName::FibexElementRef))
1778 .unwrap();
1779 assert_eq!(
1780 el_fbx_ref1.get_reference_target().unwrap().element_name(),
1781 ElementName::EcuInstance
1782 );
1783
1784 let invalid_refs = model
1785 .check_references()
1786 .iter()
1787 .filter_map(WeakElement::upgrade)
1788 .collect::<Vec<_>>();
1789 assert_eq!(invalid_refs.len(), 3);
1790 let ref0 = &invalid_refs[0];
1791 assert_eq!(ref0.element_name(), ElementName::FibexElementRef);
1792 let refpath = ref0.character_data().and_then(|cdata| cdata.string_value()).unwrap();
1793 assert!(refpath == "/Pkg/System" || refpath == "/Some/Invalid/Path");
1795
1796 model.get_element_by_path("/Pkg/EcuInstance").unwrap();
1797 let refs = model.get_references_to("/Pkg/EcuInstance");
1798 assert_eq!(refs.len(), 1);
1799 let refs = model.get_references_to("nonexistent");
1800 assert!(refs.is_empty());
1801 }
1802
1803 #[test]
1804 fn serialize_files() {
1805 let model = AutosarModel::default();
1806 let file1 = model.create_file("filename1", AutosarVersion::Autosar_00042).unwrap();
1807 let file2 = model.create_file("filename2", AutosarVersion::Autosar_00042).unwrap();
1808
1809 let result = model.serialize_files();
1810 assert_eq!(result.len(), 2);
1811 assert_eq!(
1812 result.get(&PathBuf::from("filename1")).unwrap(),
1813 &file1.serialize().unwrap()
1814 );
1815 assert_eq!(
1816 result.get(&PathBuf::from("filename2")).unwrap(),
1817 &file2.serialize().unwrap()
1818 );
1819 }
1820
1821 #[test]
1822 fn duplicate() {
1823 let model = AutosarModel::new();
1824 let file1 = model.create_file("filename1", AutosarVersion::Autosar_00042).unwrap();
1825 let file2 = model.create_file("filename2", AutosarVersion::Autosar_00042).unwrap();
1826 let el_ar_packages = model
1827 .root_element()
1828 .create_sub_element(ElementName::ArPackages)
1829 .unwrap();
1830 let el_pkg1 = el_ar_packages
1831 .create_named_sub_element(ElementName::ArPackage, "pkg1")
1832 .unwrap();
1833 let el_pkg2 = el_ar_packages
1834 .create_named_sub_element(ElementName::ArPackage, "pkg2")
1835 .unwrap();
1836
1837 assert_eq!(el_ar_packages.file_membership().unwrap().1.len(), 2);
1838 el_pkg1.remove_from_file(&file2).unwrap();
1839 assert_eq!(el_pkg1.file_membership().unwrap().1.len(), 1);
1840 el_pkg2.remove_from_file(&file1).unwrap();
1841 assert_eq!(el_pkg2.file_membership().unwrap().1.len(), 1);
1842
1843 let model2 = model.duplicate().unwrap();
1844 assert_eq!(model2.files().count(), 2);
1845 let mut files_iter = model2.files();
1846 let mut model2_file1 = files_iter.next().unwrap();
1848 let mut model2_file2 = files_iter.next().unwrap();
1849 if model2_file1.filename() != file1.filename() {
1851 std::mem::swap(&mut model2_file1, &mut model2_file2);
1852 }
1853
1854 assert_eq!(file1.filename(), model2_file1.filename());
1855 assert_eq!(file2.filename(), model2_file2.filename());
1856 assert_eq!(file1.serialize().unwrap(), model2_file1.serialize().unwrap());
1857 assert_eq!(file2.serialize().unwrap(), model2_file2.serialize().unwrap());
1858 }
1859
1860 #[test]
1861 fn write() {
1862 let model = AutosarModel::default();
1863 model.write().unwrap();
1865
1866 let dir = tempdir().unwrap();
1867 let filename = dir.path().with_file_name("new.arxml");
1868 model.create_file(&filename, AutosarVersion::LATEST).unwrap();
1869 model.write().unwrap();
1870 assert!(filename.exists());
1871
1872 let filename = PathBuf::from("nonexistent/dir/some_file.arxml");
1873 let model = AutosarModel::default();
1874 model.create_file(&filename, AutosarVersion::LATEST).unwrap();
1876 let result = model.write();
1878 assert!(result.is_err());
1879 }
1880
1881 #[test]
1882 fn traits() {
1883 let model = AutosarModel::new();
1885 let model_cloned = model.clone();
1886 assert_eq!(model, model_cloned);
1887 assert_eq!(format!("{model:#?}"), format!("{model_cloned:#?}"));
1888 let mut hashset = HashSet::<AutosarModel>::new();
1889 hashset.insert(model);
1890 let inserted = hashset.insert(model_cloned);
1891 assert!(!inserted);
1892
1893 let cdata = CharacterData::String("x".to_string());
1895 let cdata2 = cdata.clone();
1896 assert_eq!(cdata, cdata2);
1897 assert_eq!(format!("{cdata:#?}"), format!("{cdata2:#?}"));
1898
1899 let ct: ContentType = ContentType::Elements;
1901 let ct2 = ct;
1902 assert_eq!(ct, ct2);
1903 assert_eq!(format!("{ct:#?}"), format!("{ct2:#?}"));
1904 }
1905
1906 #[test]
1907 fn elements_dfs_with_max_depth() {
1908 const FILEBUF: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1909 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1910 <AR-PACKAGES>
1911 <AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
1912 <ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
1913 <SHORT-NAME>BswModuleValues</SHORT-NAME>
1914 <PARAMETER-VALUES>
1915 <ECUC-NUMERICAL-PARAM-VALUE>
1916 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
1917 </ECUC-NUMERICAL-PARAM-VALUE>
1918 <ECUC-NUMERICAL-PARAM-VALUE>
1919 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_B</DEFINITION-REF>
1920 </ECUC-NUMERICAL-PARAM-VALUE>
1921 <ECUC-NUMERICAL-PARAM-VALUE>
1922 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_C</DEFINITION-REF>
1923 </ECUC-NUMERICAL-PARAM-VALUE>
1924 </PARAMETER-VALUES>
1925 </ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
1926 </ELEMENTS></AR-PACKAGE>
1927 <AR-PACKAGE><SHORT-NAME>Pkg_B</SHORT-NAME></AR-PACKAGE>
1928 <AR-PACKAGE><SHORT-NAME>Pkg_C</SHORT-NAME></AR-PACKAGE>
1929 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
1930 let model = AutosarModel::new();
1931 let (_, _) = model.load_buffer(FILEBUF, "test1", true).unwrap();
1932 let all_count = model.elements_dfs().count();
1933 let lvl2_count = model.elements_dfs_with_max_depth(2).count();
1934 assert!(all_count > lvl2_count);
1935 for elem in model.elements_dfs_with_max_depth(2) {
1936 assert!(elem.0 <= 2);
1937 }
1938 }
1939
1940 #[test]
1941 fn model_merge() {
1942 const FILE_A: &[u8] = br#"<?xml version="1.0" encoding="utf-8"?>
1944<AUTOSAR xmlns="http://autosar.org/schema/r4.0"
1945 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1946 xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00048.xsd">
1947 <AR-PACKAGES>
1948 <AR-PACKAGE>
1949 <SHORT-NAME>EcucModuleConfigurationValuess</SHORT-NAME>
1950 <ELEMENTS>
1951 <ECUC-MODULE-CONFIGURATION-VALUES>
1952 <SHORT-NAME>A</SHORT-NAME>
1953 <DEFINITION-REF DEST="ECUC-MODULE-DEF">/AUTOSAR_A</DEFINITION-REF>
1954 <CONTAINERS>
1955 <ECUC-CONTAINER-VALUE>
1956 <SHORT-NAME>AB</SHORT-NAME>
1957 <DEFINITION-REF DEST="ECUC-PARAM-CONF-CONTAINER-DEF">/AUTOSAR_A/B</DEFINITION-REF>
1958 <PARAMETER-VALUES>
1959 <ECUC-NUMERICAL-PARAM-VALUE>
1960 <DEFINITION-REF DEST="ECUC-FLOAT-PARAM-DEF">/AUTOSAR_A/B/D</DEFINITION-REF>
1961 <VALUE>0.01</VALUE>
1962 </ECUC-NUMERICAL-PARAM-VALUE>
1963 <ECUC-TEXTUAL-PARAM-VALUE>
1964 <DEFINITION-REF DEST="ECUC-ENUMERATION-PARAM-DEF">/AUTOSAR_A/B/E</DEFINITION-REF>
1965 <VALUE>ABC42</VALUE>
1966 </ECUC-TEXTUAL-PARAM-VALUE>
1967 </PARAMETER-VALUES>
1968 </ECUC-CONTAINER-VALUE>
1969 </CONTAINERS>
1970 </ECUC-MODULE-CONFIGURATION-VALUES>
1971 </ELEMENTS>
1972 </AR-PACKAGE>
1973 </AR-PACKAGES>
1974</AUTOSAR>
1975 "#;
1976
1977 const FILE_B: &[u8] = br#"<?xml version="1.0" encoding="utf-8"?>
1978<AUTOSAR xmlns="http://autosar.org/schema/r4.0"
1979 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1980 xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00048.xsd">
1981 <AR-PACKAGES>
1982 <AR-PACKAGE>
1983 <SHORT-NAME>EcucModuleConfigurationValuess</SHORT-NAME>
1984 <ELEMENTS>
1985 <ECUC-MODULE-CONFIGURATION-VALUES>
1986 <SHORT-NAME>A</SHORT-NAME>
1987 <DEFINITION-REF DEST="ECUC-MODULE-DEF">/AUTOSAR_A</DEFINITION-REF>
1988 <CONTAINERS>
1989 <ECUC-CONTAINER-VALUE>
1990 <SHORT-NAME>AB</SHORT-NAME>
1991 <DEFINITION-REF DEST="ECUC-PARAM-CONF-CONTAINER-DEF">/AUTOSAR_A/B</DEFINITION-REF>
1992 <PARAMETER-VALUES>
1993 <ECUC-NUMERICAL-PARAM-VALUE>
1994 <DEFINITION-REF DEST="ECUC-INTEGER-PARAM-DEF">/AUTOSAR_A/B/C</DEFINITION-REF>
1995 <VALUE>0</VALUE>
1996 </ECUC-NUMERICAL-PARAM-VALUE>
1997 <ECUC-NUMERICAL-PARAM-VALUE>
1998 <DEFINITION-REF DEST="ECUC-FLOAT-PARAM-DEF">/AUTOSAR_A/B/D</DEFINITION-REF>
1999 <VALUE>0.01</VALUE>
2000 </ECUC-NUMERICAL-PARAM-VALUE>
2001 </PARAMETER-VALUES>
2002 </ECUC-CONTAINER-VALUE>
2003 </CONTAINERS>
2004 </ECUC-MODULE-CONFIGURATION-VALUES>
2005 </ELEMENTS>
2006 </AR-PACKAGE>
2007 </AR-PACKAGES>
2008</AUTOSAR>"#;
2009
2010 let model = AutosarModel::new();
2012 let (_, _) = model.load_buffer(FILE_A, "file_a", true).unwrap();
2013 let (_, _) = model.load_buffer(FILE_B, "file_b", true).unwrap();
2014 model.sort();
2016 let model_txt = model.root_element().serialize();
2017
2018 let model2 = AutosarModel::new();
2019 let (_, _) = model2.load_buffer(FILE_B, "file_b", true).unwrap();
2020 let (_, _) = model2.load_buffer(FILE_A, "file_a", true).unwrap();
2021 model2.sort();
2023 let model2_txt = model2.root_element().serialize();
2024
2025 assert_eq!(model_txt, model2_txt);
2026 }
2027
2028 #[test]
2029 fn model_merge_2() {
2030 const FILEBUF1: &[u8] = br#"<?xml version="1.0" encoding="UTF-8"?>
2032<AUTOSAR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://autosar.org/schema/r4.0" xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_4-3-0.xsd">
2033 <AR-PACKAGES>
2034 <AR-PACKAGE>
2035 <SHORT-NAME>BSWMD_Package</SHORT-NAME>
2036 <ELEMENTS>
2037 <BSW-MODULE-DESCRIPTION>
2038 <SHORT-NAME>BSWMD</SHORT-NAME>
2039 <IMPLEMENTED-ENTRYS>
2040 <BSW-MODULE-ENTRY-REF-CONDITIONAL>
2041 <BSW-MODULE-ENTRY-REF DEST="BSW-MODULE-ENTRY">/path/to/entry_A0</BSW-MODULE-ENTRY-REF>
2042 </BSW-MODULE-ENTRY-REF-CONDITIONAL>
2043 </IMPLEMENTED-ENTRYS>
2044 </BSW-MODULE-DESCRIPTION>
2045 </ELEMENTS>
2046 </AR-PACKAGE>
2047 </AR-PACKAGES>
2048</AUTOSAR>"#;
2049 const FILEBUF2: &[u8] = br#"<?xml version="1.0" encoding="UTF-8"?>
2050<AUTOSAR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://autosar.org/schema/r4.0" xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_4-3-0.xsd">
2051 <AR-PACKAGES>
2052 <AR-PACKAGE>
2053 <SHORT-NAME>BSWMD_Package</SHORT-NAME>
2054 <ELEMENTS>
2055 <BSW-MODULE-DESCRIPTION>
2056 <SHORT-NAME>BSWMD</SHORT-NAME>
2057 <IMPLEMENTED-ENTRYS>
2058 <BSW-MODULE-ENTRY-REF-CONDITIONAL>
2059 <BSW-MODULE-ENTRY-REF DEST="BSW-MODULE-ENTRY">/path/to/entry_B0</BSW-MODULE-ENTRY-REF>
2060 </BSW-MODULE-ENTRY-REF-CONDITIONAL>
2061 <BSW-MODULE-ENTRY-REF-CONDITIONAL>
2062 <BSW-MODULE-ENTRY-REF DEST="BSW-MODULE-ENTRY">/path/to/entry_B1</BSW-MODULE-ENTRY-REF>
2063 </BSW-MODULE-ENTRY-REF-CONDITIONAL>
2064 </IMPLEMENTED-ENTRYS>
2065 </BSW-MODULE-DESCRIPTION>
2066 </ELEMENTS>
2067 </AR-PACKAGE>
2068 </AR-PACKAGES>
2069</AUTOSAR>"#;
2070
2071 let model = AutosarModel::new();
2072 let (_, _) = model.load_buffer(FILEBUF1, "file1", true).unwrap();
2073 let (_, _) = model.load_buffer(FILEBUF2, "file2", true).unwrap();
2074
2075 let a0_refs = model.get_references_to("/path/to/entry_A0");
2076 assert!(a0_refs.len() == 1);
2077 assert!(a0_refs[0].upgrade().is_some());
2078 let b0_refs = model.get_references_to("/path/to/entry_B0");
2079 assert!(b0_refs.len() == 1);
2080 assert!(b0_refs[0].upgrade().is_some());
2081 let b1_refs = model.get_references_to("/path/to/entry_B1");
2082 assert!(b1_refs.len() == 1);
2083 assert!(b1_refs[0].upgrade().is_some());
2084 }
2085
2086 #[test]
2087 fn complex_reference_bases() {
2088 const FILEBUF1: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
2089<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
2090 <AR-PACKAGES>
2091 <AR-PACKAGE><SHORT-NAME>BasesPkg</SHORT-NAME>
2092 <REFERENCE-BASES>
2093 <REFERENCE-BASE>
2094 <SHORT-LABEL>BaseA</SHORT-LABEL>
2095 <PACKAGE-REF DEST="AR-PACKAGE">/ContentPkg</PACKAGE-REF>
2096 </REFERENCE-BASE>
2097 <REFERENCE-BASE>
2098 <SHORT-LABEL>BaseC</SHORT-LABEL>
2099 <PACKAGE-REF DEST="AR-PACKAGE">/ContentPkg2</PACKAGE-REF>
2100 </REFERENCE-BASE>
2101 </REFERENCE-BASES>
2102 <AR-PACKAGES>
2103 <AR-PACKAGE><SHORT-NAME>SubPackage</SHORT-NAME>
2104 <REFERENCE-BASES>
2105 <REFERENCE-BASE>
2106 <SHORT-LABEL>BaseB</SHORT-LABEL>
2107 <PACKAGE-REF DEST="AR-PACKAGE" BASE="BaseA">SubPackage</PACKAGE-REF>
2108 </REFERENCE-BASE>
2109 </REFERENCE-BASES>
2110 <ELEMENTS>
2111 <SYSTEM><SHORT-NAME>System</SHORT-NAME>
2112 <FIBEX-ELEMENTS>
2113 <FIBEX-ELEMENT-REF-CONDITIONAL>
2114 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE" BASE="BaseB">Ecu</FIBEX-ELEMENT-REF>
2115 </FIBEX-ELEMENT-REF-CONDITIONAL>
2116 <FIBEX-ELEMENT-REF-CONDITIONAL>
2117 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE" BASE="BaseC">Ecu</FIBEX-ELEMENT-REF>
2118 </FIBEX-ELEMENT-REF-CONDITIONAL>
2119 </FIBEX-ELEMENTS>
2120 </SYSTEM>
2121 </ELEMENTS>
2122 </AR-PACKAGE>
2123 </AR-PACKAGES>
2124 </AR-PACKAGE>
2125 <AR-PACKAGE><SHORT-NAME>ContentPkg</SHORT-NAME>
2126 <AR-PACKAGES>
2127 <AR-PACKAGE><SHORT-NAME>SubPackage</SHORT-NAME>
2128 <ELEMENTS>
2129 <ECU-INSTANCE><SHORT-NAME>Ecu</SHORT-NAME></ECU-INSTANCE>
2130 </ELEMENTS>
2131 </AR-PACKAGE>
2132 </AR-PACKAGES>
2133 </AR-PACKAGE>
2134 <AR-PACKAGE><SHORT-NAME>ContentPkg2</SHORT-NAME>
2135 <ELEMENTS>
2136 <ECU-INSTANCE><SHORT-NAME>Ecu</SHORT-NAME></ECU-INSTANCE>
2137 </ELEMENTS>
2138 </AR-PACKAGE>
2139 </AR-PACKAGES>
2140</AUTOSAR>"#.as_bytes();
2141 let model = AutosarModel::new();
2142 let result = model.load_buffer(FILEBUF1, "test", true);
2143 assert!(result.is_ok());
2144
2145 let el_system = model.get_element_by_path("/BasesPkg/SubPackage/System").unwrap();
2146 let el_fibex_elements = el_system.get_sub_element(ElementName::FibexElements).unwrap();
2147 let el_fibex_element_ref = el_fibex_elements
2148 .get_sub_element_at(0)
2149 .and_then(|ferc| ferc.get_sub_element(ElementName::FibexElementRef))
2150 .unwrap();
2151
2152 let el_ecu = el_fibex_element_ref.get_reference_target().unwrap();
2154 assert_eq!(el_ecu.element_name(), ElementName::EcuInstance);
2155
2156 let origins = model.get_references_to(&el_ecu.path().unwrap());
2158 assert_eq!(origins.len(), 1);
2159 let origin = origins[0].upgrade().unwrap();
2160 assert_eq!(origin.element_name(), ElementName::FibexElementRef);
2161
2162 let origins2 = model.get_references_to("/ContentPkg2/Ecu");
2163 assert_eq!(origins2.len(), 1);
2164 }
2165}