Skip to main content

autosar_data/
element.rs

1use std::cmp::Ordering;
2use std::hash::Hash;
3use std::str::FromStr;
4
5use super::*;
6
7impl Element {
8    /// Get the parent element of the current element
9    ///
10    /// Returns None if the current element is the root, or if it has been deleted from the element hierarchy
11    ///
12    /// # Example
13    ///
14    /// ```
15    /// # use autosar_data::*;
16    /// # fn main() -> Result<(), AutosarDataError> {
17    /// # let model = AutosarModel::new();
18    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
19    /// # let element = model.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
20    /// if let Some(parent) = element.parent()? {
21    ///     // ...
22    /// }
23    /// # Ok(())
24    /// # }
25    /// ```
26    ///
27    /// # Errors
28    ///
29    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
30    pub fn parent(&self) -> Result<Option<Element>, AutosarDataError> {
31        self.0.read().parent()
32    }
33
34    /// Get the next named parent (or grandparent, etc) of the current element
35    ///
36    /// This function steps through the hierarchy until an identifiable element is found.
37    /// It never returns the current element, even if the current element is identifiable.
38    ///
39    /// The function returns a suitable element if one is found, or None if the root is reached.
40    ///
41    /// # Example
42    ///
43    /// ```
44    /// # use autosar_data::*;
45    /// # fn main() -> Result<(), AutosarDataError> {
46    /// # let model = AutosarModel::new();
47    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050)?;
48    /// # let el_arpackage = model.root_element().create_sub_element(ElementName::ArPackages)?.create_named_sub_element(ElementName::ArPackage, "Pkg")?;
49    /// let el_elements = el_arpackage.create_sub_element(ElementName::Elements)?;
50    /// let el_system = el_elements.create_named_sub_element(ElementName::System, "Sys")?;
51    /// let named_parent = el_elements.named_parent()?.unwrap();
52    /// let named_parent2 = el_system.named_parent()?.unwrap();
53    /// assert_eq!(named_parent, el_arpackage);
54    /// assert_eq!(named_parent2, el_arpackage);
55    /// # Ok(())
56    /// # }
57    /// ```
58    ///
59    /// # Errors
60    ///
61    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
62    pub fn named_parent(&self) -> Result<Option<Element>, AutosarDataError> {
63        let mut cur_elem_opt = self.parent()?;
64        while let Some(parent) = cur_elem_opt {
65            if parent.is_identifiable() {
66                return Ok(Some(parent));
67            }
68            cur_elem_opt = parent.parent()?;
69        }
70
71        Ok(None)
72    }
73
74    pub(crate) fn set_parent(&self, new_parent: ElementOrModel) {
75        self.0.write().set_parent(new_parent);
76    }
77
78    /// Get the [`ElementName`] of the element
79    ///
80    /// # Example
81    ///
82    /// ```
83    /// # use autosar_data::*;
84    /// # let model = AutosarModel::new();
85    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
86    /// let element = model.root_element();
87    /// let element_name = element.element_name();
88    /// assert_eq!(element_name, ElementName::Autosar);
89    /// ```
90    #[must_use]
91    pub fn element_name(&self) -> ElementName {
92        self.0.read().elemname
93    }
94
95    /// Get the [`ElementType`] of the element
96    ///
97    /// The `ElementType` is needed in order to call methods from the autosar-data-specification crate
98    ///
99    /// # Example
100    ///
101    /// ```
102    /// # use autosar_data::*;
103    /// # let model = AutosarModel::new();
104    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
105    /// # let element = model.root_element();
106    /// let element_type = element.element_type();
107    /// ```
108    #[must_use]
109    pub fn element_type(&self) -> ElementType {
110        self.0.read().elemtype
111    }
112
113    /// Get the name of an identifiable element
114    ///
115    /// An identifiable element has a `<SHORT-NAME>` sub element and can be referenced using an autosar path.
116    ///
117    /// If the element is not identifiable, this function returns None
118    ///
119    /// # Example
120    ///
121    /// ```
122    /// # use autosar_data::*;
123    /// # let model = AutosarModel::new();
124    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
125    /// # let element = model.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
126    /// if let Some(item_name) = element.item_name() {
127    ///     // ...
128    /// }
129    /// ```
130    #[must_use]
131    pub fn item_name(&self) -> Option<String> {
132        self.0.read().item_name()
133    }
134
135    /// Set the item name of this element
136    ///
137    /// This operation will update all references pointing to the element or its sub-elements so that they remain valid.
138    ///
139    /// # Example
140    ///
141    /// ```
142    /// # use autosar_data::*;
143    /// # let model = AutosarModel::new();
144    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
145    /// # let element = model.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
146    /// element.set_item_name("NewName");
147    /// ```
148    ///
149    /// # Note
150    ///
151    /// In order to rename an element *without* updating any references, do this instead:
152    /// ```
153    /// # use autosar_data::*;
154    /// # let model = AutosarModel::new();
155    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
156    /// # let element = model.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
157    /// if let Some(short_name) = element.get_sub_element(ElementName::ShortName) {
158    ///     short_name.set_character_data("the_new_name");
159    /// }
160    /// ```
161    ///
162    /// # Errors
163    ///
164    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
165    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
166    ///    The operation was aborted to avoid a deadlock, but can be retried.
167    ///  - [`AutosarDataError::ItemNameRequired`] this function was called for an element which is not identifiable
168    ///
169    pub fn set_item_name(&self, new_name: &str) -> Result<(), AutosarDataError> {
170        // a new name is required
171        if new_name.is_empty() {
172            return Err(AutosarDataError::ItemNameRequired {
173                element: self.element_name(),
174            });
175        }
176        let model = self.model()?;
177        let version = self.min_version()?;
178        self.0.write().set_item_name(new_name, &model, version)
179    }
180
181    /// Returns true if the element is identifiable
182    ///
183    /// In order to be identifiable, the specification must require a SHORT-NAME
184    /// sub-element and the SHORT-NAME must actually be present.
185    ///
186    /// # Example
187    /// ```
188    /// # use autosar_data::*;
189    /// # let model = AutosarModel::new();
190    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
191    /// # let element = model.root_element();
192    /// if element.is_identifiable() {
193    ///     // ...
194    /// }
195    /// ```
196    #[must_use]
197    pub fn is_identifiable(&self) -> bool {
198        self.0.read().is_identifiable()
199    }
200
201    /// Returns true if the element should contain a reference to another element
202    ///
203    /// The function does not check if the reference is valid
204    ///
205    /// # Example
206    ///
207    /// ```
208    /// # use autosar_data::*;
209    /// # let model = AutosarModel::new();
210    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
211    /// # let element = model.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
212    /// if element.is_reference() {
213    ///     // ex: element.set_reference_target(...)
214    /// }
215    /// ```
216    #[must_use]
217    pub fn is_reference(&self) -> bool {
218        self.elemtype().is_ref()
219    }
220
221    /// Get the Autosar path of an identifiable element
222    ///
223    /// # Example
224    ///
225    /// ```
226    /// # use autosar_data::*;
227    /// # fn main() -> Result<(), AutosarDataError> {
228    /// # let model = AutosarModel::new();
229    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
230    /// # let element = model.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
231    /// let path = element.path()?;
232    /// # Ok(())
233    /// # }
234    /// ```
235    ///
236    /// # Errors
237    ///
238    ///  - [`AutosarDataError::ItemDeleted`]: Th ecurrent element is in the deleted state and will be freed once the last reference is dropped
239    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
240    ///    The operation was aborted to avoid a deadlock, but can be retried.
241    ///  - [`AutosarDataError::ElementNotIdentifiable`]: The current element is not identifiable, so it has no Autosar path
242    ///
243    pub fn path(&self) -> Result<String, AutosarDataError> {
244        self.0.read().path()
245    }
246
247    /// Get the package element containing the current element
248    ///
249    /// It never returns the current element, even if the current element is a package, but always
250    /// goes up the hierarchy to find the nearest parent package.
251    ///
252    /// Returns
253    ///   Ok(Some(Element)) if a parent package is found
254    ///   Ok(None) at the top level.
255    ///   Err if the action cannot be completed
256    ///
257    /// # Example
258    ///
259    /// ```
260    /// # use autosar_data::*;
261    /// # fn main() -> Result<(), AutosarDataError> {
262    /// # let model = AutosarModel::new();
263    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
264    /// # let element = model.root_element().create_sub_element(ElementName::ArPackages)?.create_named_sub_element(ElementName::ArPackage, "Pkg")?;
265    /// let package = element.package()?;
266    /// # Ok(())
267    /// # }
268    /// ```
269    ///
270    /// # Errors
271    ///
272    /// - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
273    /// - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
274    ///   The operation was aborted to avoid a deadlock, but can be retried.
275    pub fn package(&self) -> Result<Option<Element>, AutosarDataError> {
276        let mut cur_elem = self.clone();
277        loop {
278            let parent = {
279                let element = cur_elem
280                    .0
281                    .try_read_for(std::time::Duration::from_millis(10))
282                    .ok_or(AutosarDataError::ParentElementLocked)?;
283                match &element.parent {
284                    ElementOrModel::Element(weak_parent) => {
285                        let parent = weak_parent.upgrade().ok_or(AutosarDataError::ItemDeleted)?;
286                        if parent.element_name() == ElementName::ArPackage {
287                            return Ok(Some(parent));
288                        }
289                        parent
290                    }
291                    ElementOrModel::Model(_) => {
292                        return Ok(None);
293                    }
294                    ElementOrModel::None => return Err(AutosarDataError::ItemDeleted),
295                }
296            };
297            cur_elem = parent;
298        }
299    }
300
301    /// Get a reference to the [`AutosarModel`] containing the current element
302    ///
303    /// # Example
304    ///
305    /// ```
306    /// # use autosar_data::*;
307    /// # fn main() -> Result<(), AutosarDataError> {
308    /// # let model = AutosarModel::new();
309    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
310    /// # let element = model.root_element().create_sub_element(ElementName::ArPackages).and_then(|pkgs| pkgs.create_named_sub_element(ElementName::ArPackage, "name")).unwrap();
311    /// let file = element.model()?;
312    /// # Ok(())
313    /// # }
314    /// ```
315    ///
316    /// # Errors
317    ///
318    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
319    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
320    ///    The operation was aborted to avoid a deadlock, but can be retried.
321    ///
322    pub fn model(&self) -> Result<AutosarModel, AutosarDataError> {
323        let mut cur_elem = self.clone();
324        loop {
325            let parent = {
326                let element = cur_elem
327                    .0
328                    .try_read_for(std::time::Duration::from_millis(10))
329                    .ok_or(AutosarDataError::ParentElementLocked)?;
330                match &element.parent {
331                    ElementOrModel::Element(weak_parent) => {
332                        weak_parent.upgrade().ok_or(AutosarDataError::ItemDeleted)?
333                    }
334                    ElementOrModel::Model(weak_arxmlfile) => {
335                        return weak_arxmlfile.upgrade().ok_or(AutosarDataError::ItemDeleted);
336                    }
337                    ElementOrModel::None => return Err(AutosarDataError::ItemDeleted),
338                }
339            };
340            cur_elem = parent;
341        }
342    }
343
344    /// Get the [`ContentType`] of the current element
345    ///
346    /// # Example
347    ///
348    /// ```
349    /// # use autosar_data::*;
350    /// # let model = AutosarModel::new();
351    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
352    /// # let element = model.root_element();
353    /// if element.content_type() == ContentType::CharacterData {
354    ///     // ...
355    /// }
356    /// ```
357    #[must_use]
358    pub fn content_type(&self) -> ContentType {
359        match self.elemtype().content_mode() {
360            ContentMode::Sequence => ContentType::Elements,
361            ContentMode::Choice => ContentType::Elements,
362            ContentMode::Bag => ContentType::Elements,
363            ContentMode::Characters => ContentType::CharacterData,
364            ContentMode::Mixed => ContentType::Mixed,
365        }
366    }
367
368    /// Create a sub element at a suitable insertion position
369    ///
370    /// The given `ElementName` must be allowed on a sub element in this element, taking into account any sub elements that may already exist.
371    /// It is not possible to create named sub elements with this function; use `create_named_sub_element`() for that instead.
372    ///
373    /// # Example
374    ///
375    /// ```
376    /// # use autosar_data::*;
377    /// # fn main() -> Result<(), AutosarDataError> {
378    /// # let model = AutosarModel::new();
379    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
380    /// let element = model.root_element().create_sub_element(ElementName::ArPackages)?;
381    /// # Ok(())
382    /// # }
383    /// ```
384    ///
385    /// # Errors
386    ///
387    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
388    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
389    ///    The operation was aborted to avoid a deadlock, but can be retried.
390    ///  - [`AutosarDataError::IncorrectContentType`]: A sub element may not be created in an element with content type `CharacterData`.
391    ///  - [`AutosarDataError::ElementInsertionConflict`]: The requested sub element cannot be created because it conflicts with an existing sub element.
392    ///  - [`AutosarDataError::InvalidSubElement`]: The `ElementName` is not a valid sub element according to the specification.
393    ///  - [`AutosarDataError::ItemNameRequired`]: The sub element requires an item name, so you must use `create_named_sub_element`().
394    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
395    pub fn create_sub_element(&self, element_name: ElementName) -> Result<Element, AutosarDataError> {
396        let version = self.min_version()?;
397        self.0
398            .try_write()
399            .ok_or(AutosarDataError::ParentElementLocked)?
400            .create_sub_element(self.downgrade(), element_name, version)
401    }
402
403    /// Create a sub element at the specified insertion position
404    ///
405    /// The given `ElementName` must be allowed on a sub element in this element, taking into account any sub elements that may already exist.
406    /// It is not possible to create named sub elements with this function; use `create_named_sub_element_at`() for that instead.
407    ///
408    /// The specified insertion position will be compared to the range of valid insertion positions; if it falls outside that range then the function fails.
409    ///
410    /// # Example
411    ///
412    /// ```
413    /// # use autosar_data::*;
414    /// # fn main() -> Result<(), AutosarDataError> {
415    /// # let model = AutosarModel::new();
416    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
417    /// let element = model.root_element().create_sub_element_at(ElementName::ArPackages, 0)?;
418    /// # Ok(())
419    /// # }
420    /// ```
421    ///
422    /// # Errors
423    ///
424    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
425    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
426    ///    The operation was aborted to avoid a deadlock, but can be retried.
427    ///  - [`AutosarDataError::IncorrectContentType`]: A sub element may not be created in an element with content type `CharacterData`.
428    ///  - [`AutosarDataError::ElementInsertionConflict`]: The requested sub element cannot be created because it conflicts with an existing sub element.
429    ///  - [`AutosarDataError::InvalidSubElement`]: The `ElementName` is not a valid sub element according to the specification.
430    ///  - [`AutosarDataError::ItemNameRequired`]: The sub element requires an item name, so you must use `create_named_sub_element_at`().
431    ///  - [`AutosarDataError::InvalidPosition`]: This sub element cannot be created at the requested position
432    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
433    pub fn create_sub_element_at(
434        &self,
435        element_name: ElementName,
436        position: usize,
437    ) -> Result<Element, AutosarDataError> {
438        let version = self.min_version()?;
439        self.0
440            .write()
441            .create_sub_element_at(self.downgrade(), element_name, position, version)
442    }
443
444    /// Create a named/identifiable sub element at a suitable insertion position
445    ///
446    /// The given `ElementName` must be allowed on a sub element in this element, taking into account any sub elements that may already exist.
447    ///
448    /// This method can only be used to create identifiable sub elements.
449    ///
450    /// # Example
451    ///
452    /// ```
453    /// # use autosar_data::*;
454    /// # fn main() -> Result<(), AutosarDataError> {
455    /// # let model = AutosarModel::new();
456    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
457    /// let pkgs_element = model.root_element().create_sub_element(ElementName::ArPackages)?;
458    /// let element = pkgs_element.create_named_sub_element(ElementName::ArPackage, "Package")?;
459    /// # Ok(())
460    /// # }
461    /// ```
462    ///
463    /// # Errors
464    ///
465    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
466    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
467    ///    The operation was aborted to avoid a deadlock, but can be retried.
468    ///  - [`AutosarDataError::IncorrectContentType`]: A sub element may not be created in an element with content type `CharacterData`.
469    ///  - [`AutosarDataError::ElementInsertionConflict`]: The requested sub element cannot be created because it conflicts with an existing sub element.
470    ///  - [`AutosarDataError::InvalidSubElement`]: The `ElementName` is not a valid sub element according to the specification.
471    ///  - [`AutosarDataError::ElementNotIdentifiable`]: The sub element does not have an item name, so you must use `create_sub_element`() instead.
472    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
473    pub fn create_named_sub_element(
474        &self,
475        element_name: ElementName,
476        item_name: &str,
477    ) -> Result<Element, AutosarDataError> {
478        let model = self.model()?;
479        let version = self.min_version()?;
480        self.0
481            .write()
482            .create_named_sub_element(self.downgrade(), element_name, item_name, &model, version)
483    }
484
485    /// Create a named/identifiable sub element at the specified insertion position
486    ///
487    /// The given `ElementName` must be allowed on a sub element in this element, taking into account any sub elements that may already exist.
488    /// The specified insertion position will be compared to the range of valid insertion positions; if it falls outside that range then the function fails.
489    ///
490    /// This method can only be used to create identifiable sub elements.
491    ///
492    /// # Example
493    ///
494    /// ```
495    /// # use autosar_data::*;
496    /// # fn main() -> Result<(), AutosarDataError> {
497    /// # let model = AutosarModel::new();
498    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
499    /// let pkgs_element = model.root_element().create_sub_element(ElementName::ArPackages)?;
500    /// let element = pkgs_element.create_named_sub_element_at(ElementName::ArPackage, "Package", 0)?;
501    /// # Ok(())
502    /// # }
503    /// ```
504    ///
505    /// # Errors
506    ///
507    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
508    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
509    ///    The operation was aborted to avoid a deadlock, but can be retried.
510    ///  - [`AutosarDataError::IncorrectContentType`]: A sub element may not be created in an element with content type `CharacterData`.
511    ///  - [`AutosarDataError::ElementInsertionConflict`]: The requested sub element cannot be created because it conflicts with an existing sub element.
512    ///  - [`AutosarDataError::InvalidSubElement`]: The `ElementName` is not a valid sub element according to the specification.
513    ///  - [`AutosarDataError::ElementNotIdentifiable`]: The sub element does not have an item name, so you must use `create_sub_element`() instead.
514    ///  - [`AutosarDataError::InvalidPosition`]: This sub element cannot be created at the requested position.
515    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
516    pub fn create_named_sub_element_at(
517        &self,
518        element_name: ElementName,
519        item_name: &str,
520        position: usize,
521    ) -> Result<Element, AutosarDataError> {
522        let model = self.model()?;
523        let version = self.min_version()?;
524        self.0
525            .write()
526            .create_named_sub_element_at(self.downgrade(), element_name, item_name, position, &model, version)
527    }
528
529    /// Create a deep copy of the given element and insert it as a sub-element
530    ///
531    /// The other element must be a permissible sub-element in this element and not conflict with any existing sub element.
532    /// The other element can originate from any loaded [`AutosarModel`], it does not have to originate from the same model or file as the current element.
533    ///
534    /// The [`AutosarVersion`] of the other element might differ from the version of the current file;
535    /// in this case a partial copy will be performed that omits all incompatible elements.
536    ///
537    /// If the copied element is identifiable, then the item name might be extended with a numerical suffix, if one is required in order to make the name unique.
538    /// For example: An identifiable element "Foo" already exists at the same path; the copied identifiable element will be renamed to "`Foo_1`".
539    ///
540    /// If the copied element or the hierarchy of elements under it contain any references, then these will need to be adjusted manually after copying.
541    ///
542    /// # Example
543    ///
544    /// ```
545    /// # use autosar_data::*;
546    /// # fn main() -> Result<(), AutosarDataError> {
547    /// # let model = AutosarModel::new();
548    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
549    /// # let pkgs_element = model.root_element().create_sub_element(ElementName::ArPackages)?;
550    /// # let base = pkgs_element.create_named_sub_element(ElementName::ArPackage, "Package")
551    /// #    .and_then(|p| p.create_sub_element(ElementName::Elements))?;
552    /// # base.create_named_sub_element(ElementName::System, "Path")?;
553    /// let other_element = model.get_element_by_path("/Package/Path").unwrap();
554    /// let element = base.create_copied_sub_element(&other_element)?;
555    /// # Ok(())
556    /// # }
557    /// ```
558    ///
559    /// # Errors
560    ///
561    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
562    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
563    ///    The operation was aborted to avoid a deadlock, but can be retried.
564    ///  - [`AutosarDataError::IncorrectContentType`]: A sub element may not be created in an element with content type `CharacterData`.
565    ///  - [`AutosarDataError::ElementInsertionConflict`]: The requested sub element cannot be created because it conflicts with an existing sub element.
566    ///  - [`AutosarDataError::InvalidSubElement`]: The `ElementName` is not a valid sub element according to the specification.
567    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
568    pub fn create_copied_sub_element(&self, other: &Element) -> Result<Element, AutosarDataError> {
569        if self == other {
570            // trying to copy self into self never makes sense, and would deadlock
571            return Err(AutosarDataError::InvalidSubElement {
572                parent: self.element_name(),
573                element: self.element_name(),
574            });
575        }
576        let model = self.model()?;
577        let version = self.min_version()?;
578        self.0
579            .write()
580            .create_copied_sub_element(self.downgrade(), other, &model, version)
581    }
582
583    /// Create a deep copy of the given element and insert it as a sub-element at the given position
584    ///
585    /// The other element must be a permissible sub-element in this element and not conflict with any existing sub element.
586    /// The other element can originate from any loaded [`AutosarModel`], it does not have to originate from the same model or file as the current element.
587    ///
588    /// The [`AutosarVersion`] of the other element might differ from the version of the current file;
589    /// in this case a partial copy will be performed that omits all incompatible elements.
590    ///
591    /// If the copied element is identifiable, then the item name might be extended with a numerical suffix, if one is required in order to make the name unique.
592    /// For example: An identifiable element "Foo" already exists at the same path; the copied identifiable element will be renamed to "`Foo_1`".
593    ///
594    /// If the copied element or the hierarchy of elements under it contain any references, then these will need to be adjusted manually after copying.
595    ///
596    /// # Example
597    ///
598    /// ```
599    /// # use autosar_data::*;
600    /// # fn main() -> Result<(), AutosarDataError> {
601    /// # let model = AutosarModel::new();
602    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
603    /// # let pkgs_element = model.root_element().create_sub_element(ElementName::ArPackages)?;
604    /// # let base = pkgs_element.create_named_sub_element(ElementName::ArPackage, "Package")
605    /// #    .and_then(|pkg| pkg.create_sub_element(ElementName::Elements))?;
606    /// # base.create_named_sub_element(ElementName::System, "Path")?;
607    /// let other_element = model.get_element_by_path("/Package/Path").unwrap();
608    /// let element = base.create_copied_sub_element_at(&other_element, 0)?;
609    /// # Ok(())
610    /// # }
611    /// ```
612    ///
613    /// # Errors
614    ///
615    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
616    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
617    ///    The operation was aborted to avoid a deadlock, but can be retried.
618    ///  - [`AutosarDataError::IncorrectContentType`]: A sub element may not be created in an element with content type `CharacterData`.
619    ///  - [`AutosarDataError::ElementInsertionConflict`]: The requested sub element cannot be created because it conflicts with an existing sub element.
620    ///  - [`AutosarDataError::InvalidSubElement`]: The `ElementName` is not a valid sub element according to the specification.
621    ///  - [`AutosarDataError::InvalidPosition`]: This sub element cannot be created at the requested position.
622    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
623    pub fn create_copied_sub_element_at(&self, other: &Element, position: usize) -> Result<Element, AutosarDataError> {
624        if self == other {
625            // trying to copy self into self never makes sense, and would deadlock
626            return Err(AutosarDataError::InvalidSubElement {
627                parent: self.element_name(),
628                element: self.element_name(),
629            });
630        }
631        let model = self.model()?;
632        let version = self.min_version()?;
633        self.0
634            .write()
635            .create_copied_sub_element_at(self.downgrade(), other, position, &model, version)
636    }
637
638    /// Take an `element` from it's current location and place it in this element as a sub element
639    ///
640    /// The moved element can be taken from anywhere - even from a different arxml document that is not part of the same `AutosarModel`
641    ///
642    /// Restrictions:
643    /// 1) The element must have a compatible element type. If it could not have been created here, then it can't be moved either.
644    /// 2) The origin document of the element must have exactly the same `AutosarVersion` as the destination.
645    ///
646    /// # Example
647    ///
648    /// ```
649    /// # use autosar_data::*;
650    /// # fn main() -> Result<(), AutosarDataError> {
651    /// # let model = AutosarModel::new();
652    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
653    /// # let pkgs_element = model.root_element().create_sub_element(ElementName::ArPackages)?;
654    /// # let base = pkgs_element.create_named_sub_element(ElementName::ArPackage, "Package")
655    /// #    .and_then(|pkg| pkg.create_sub_element(ElementName::Elements))?;
656    /// # base.create_named_sub_element(ElementName::System, "Path")?;
657    /// let other_element = model.get_element_by_path("/Package/Path").unwrap();
658    /// let element = base.move_element_here(&other_element)?;
659    /// # Ok(())
660    /// # }
661    /// ```
662    ///
663    /// # Errors
664    ///
665    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
666    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
667    ///    The operation was aborted to avoid a deadlock, but can be retried.
668    ///  - [`AutosarDataError::IncorrectContentType`]: A sub element may not be created in an element with content type `CharacterData`.
669    ///  - [`AutosarDataError::ElementInsertionConflict`]: The requested sub element cannot be created because it conflicts with an existing sub element.
670    ///  - [`AutosarDataError::InvalidSubElement`]: The `ElementName` is not a valid sub element according to the specification.
671    ///  - [`AutosarDataError::VersionMismatch`]: The Autosar versions of the source and destination are different
672    ///  - [`AutosarDataError::ForbiddenMoveToSubElement`]: The destination is a sub element of the source. Moving here is not possible
673    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
674    pub fn move_element_here(&self, move_element: &Element) -> Result<Element, AutosarDataError> {
675        let model_src = move_element.model()?;
676        let model = self.model()?;
677        let version_src = move_element.min_version()?;
678        let version = self.min_version()?;
679        if version != version_src {
680            return Err(AutosarDataError::VersionMismatch {
681                version_cur: version,
682                version_new: version_src,
683            });
684        }
685        self.0
686            .write()
687            .move_element_here(self.downgrade(), move_element, &model, &model_src, version)
688    }
689
690    /// Take an `element` from it's current location and place it at the given position in this element as a sub element
691    ///
692    /// The moved element can be taken from anywhere - even from a different arxml document that is not part of the same `AutosarModel`
693    ///
694    /// Restrictions:
695    /// 1) The element must have a compatible element type. If it could not have been created here, then it can't be moved either.
696    /// 2) The origin document of the element must have exactly the same `AutosarVersion` as the destination.
697    ///
698    /// # Example
699    ///
700    /// ```
701    /// # use autosar_data::*;
702    /// # fn main() -> Result<(), AutosarDataError> {
703    /// # let model = AutosarModel::new();
704    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
705    /// # let pkgs_element = model.root_element().create_sub_element(ElementName::ArPackages)?;
706    /// # let base = pkgs_element.create_named_sub_element(ElementName::ArPackage, "Package")
707    /// #    .and_then(|p| p.create_sub_element(ElementName::Elements))?;
708    /// # base.create_named_sub_element(ElementName::System, "Path")?;
709    /// let other_element = model.get_element_by_path("/Package/Path").unwrap();
710    /// let element = base.move_element_here_at(&other_element, 0)?;
711    /// # Ok(())
712    /// # }
713    /// ```
714    ///
715    /// # Errors
716    ///
717    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
718    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
719    ///    The operation was aborted to avoid a deadlock, but can be retried.
720    ///  - [`AutosarDataError::IncorrectContentType`]: A sub element may not be created in an element with content type `CharacterData`.
721    ///  - [`AutosarDataError::ElementInsertionConflict`]: The requested sub element cannot be created because it conflicts with an existing sub element.
722    ///  - [`AutosarDataError::InvalidSubElement`]: The `ElementName` is not a valid sub element according to the specification.
723    ///  - [`AutosarDataError::VersionMismatch`]: The Autosar versions of the source and destination are different
724    ///  - [`AutosarDataError::ForbiddenMoveToSubElement`]: The destination is a sub element of the source. Moving here is not possible
725    ///  - [`AutosarDataError::InvalidPosition`]: This sub element cannot be created at the requested position.
726    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
727    pub fn move_element_here_at(&self, move_element: &Element, position: usize) -> Result<Element, AutosarDataError> {
728        let model_src = move_element.model()?;
729        let model = self.model()?;
730        let version_src = move_element.min_version()?;
731        let version = self.min_version()?;
732        if version != version_src {
733            return Err(AutosarDataError::VersionMismatch {
734                version_cur: version,
735                version_new: version_src,
736            });
737        }
738        self.0
739            .write()
740            .move_element_here_at(self.downgrade(), move_element, position, &model, &model_src, version)
741    }
742
743    /// Remove the sub element `sub_element`
744    ///
745    /// The `sub_element` will be unlinked from the hierarchy of elements.
746    /// All of the sub-sub-elements nested under the removed element will also be recusively removed.
747    ///
748    /// Since all elements are reference counted, they might not be deallocated immediately, however they do become invalid and unusable immediately.
749    ///
750    /// # Example
751    ///
752    /// ```
753    /// # use autosar_data::*;
754    /// # fn main() -> Result<(), AutosarDataError> {
755    /// # let model = AutosarModel::new();
756    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
757    /// let packages = model.root_element().create_sub_element(ElementName::ArPackages)?;
758    /// model.root_element().remove_sub_element(packages)?;
759    /// # Ok(())
760    /// # }
761    /// ```
762    ///
763    /// # Errors
764    ///
765    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
766    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
767    ///    The operation was aborted to avoid a deadlock, but can be retried.
768    ///  - [`AutosarDataError::ElementNotFound`]: The sub element was not found in this element
769    ///  - [`AutosarDataError::ShortNameRemovalForbidden`]: It is not permitted to remove the SHORT-NAME of identifiable elements since this would result in invalid data
770    pub fn remove_sub_element(&self, sub_element: Element) -> Result<(), AutosarDataError> {
771        let model = self.model()?;
772        self.0.write().remove_sub_element(sub_element, &model)
773    }
774
775    /// Remove a sub element identified by an ElementName
776    ///
777    /// If multiple sub elements with the same ElementName exist, only the first one will be removed.
778    ///
779    /// A sub element with the given ElementName will be unlinked from the hierarchy of elements.
780    /// All of the sub-sub-elements nested under the removed element will also be recusively removed.
781    ///
782    /// This is a convenience function that is equivalent to calling `get_sub_element()` followed by `remove_sub_element()`.
783    ///
784    /// # Example
785    ///
786    /// ```
787    /// # use autosar_data::*;
788    /// # fn main() -> Result<(), AutosarDataError> {
789    /// # let model = AutosarModel::new();
790    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
791    /// let packages = model.root_element().create_sub_element(ElementName::ArPackages)?;
792    /// model.root_element().remove_sub_element_kind(ElementName::ArPackages)?;
793    /// # Ok(())
794    /// # }
795    /// ```
796    ///
797    /// # Errors
798    ///
799    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
800    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
801    ///    The operation was aborted to avoid a deadlock, but can be retried.
802    ///  - [`AutosarDataError::ElementNotFound`]: The sub element was not found in this element
803    ///  - [`AutosarDataError::ShortNameRemovalForbidden`]: It is not permitted to remove the SHORT-NAME of identifiable elements since this would result in invalid data
804    pub fn remove_sub_element_kind(&self, element_name: ElementName) -> Result<(), AutosarDataError> {
805        let Some(sub_element) = self.get_sub_element(element_name) else {
806            return Err(AutosarDataError::ElementNotFound {
807                target: element_name,
808                parent: self.element_name(),
809            });
810        };
811        self.remove_sub_element(sub_element)
812    }
813
814    /// Set the reference target for the element to target
815    ///
816    /// When the reference is updated, the DEST attribute is also updated to match the referenced element.
817    /// The current element must be a reference element, otherwise the function fails.
818    ///
819    /// # Example
820    ///
821    /// ```
822    /// # use autosar_data::*;
823    /// # fn main() -> Result<(), AutosarDataError> {
824    /// # let model = AutosarModel::new();
825    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
826    /// # let elements = model.root_element().create_sub_element(ElementName::ArPackages)
827    /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))
828    /// #   .and_then(|e| e.create_sub_element(ElementName::Elements))?;
829    /// # let ref_element = elements.create_named_sub_element(ElementName::System, "System")
830    /// #   .and_then(|e| e.create_sub_element(ElementName::FibexElements))
831    /// #   .and_then(|e| e.create_sub_element(ElementName::FibexElementRefConditional))
832    /// #   .and_then(|e| e.create_sub_element(ElementName::FibexElementRef))?;
833    /// let cluster_element = elements.create_named_sub_element(ElementName::CanCluster, "Cluster")?;
834    /// ref_element.set_reference_target(&cluster_element)?;
835    /// # Ok(())
836    /// # }
837    /// ```
838    ///
839    /// # Errors
840    ///
841    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
842    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
843    ///    The operation was aborted to avoid a deadlock, but can be retried.
844    ///  - [`AutosarDataError::NotReferenceElement`]: The current element is not a reference, so it is not possible to set a reference target
845    ///  - [`AutosarDataError::InvalidReference`]: The target element is not a valid reference target for this reference
846    ///  - [`AutosarDataError::ElementNotIdentifiable`]: The target element is not identifiable, so it cannot be referenced by an Autosar path
847    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
848    pub fn set_reference_target(&self, target: &Element) -> Result<(), AutosarDataError> {
849        self.set_reference_target_internal(target, None)
850    }
851
852    /// Set the reference target using a relative path and explicit BASE label
853    ///
854    /// This method behaves like [`Self::set_reference_target`], but it writes
855    /// a relative reference path and sets the BASE attribute to `base_label`.
856    ///
857    /// # Errors
858    ///
859    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
860    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
861    ///    The operation was aborted to avoid a deadlock, but can be retried.
862    ///  - [`AutosarDataError::NotReferenceElement`]: The current element is not a reference, so it is not possible to set a reference target
863    ///  - [`AutosarDataError::InvalidReference`]: The target element is not a valid reference target for this reference
864    ///  - [`AutosarDataError::InvalidReferenceBase`]: The target element uses an invalid base label
865    ///  - [`AutosarDataError::ElementNotIdentifiable`]: The target element is not identifiable, so it cannot be referenced by an Autosar path
866    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
867    pub fn set_relative_reference_target(&self, target: &Element, base_label: &str) -> Result<(), AutosarDataError> {
868        self.set_reference_target_internal(target, Some(base_label))
869    }
870
871    fn set_reference_target_internal(
872        &self,
873        target: &Element,
874        base_label: Option<&str>,
875    ) -> Result<(), AutosarDataError> {
876        // the current element must be a reference
877        if !self.is_reference() {
878            return Err(AutosarDataError::NotReferenceElement);
879        }
880
881        // the target element must be identifiable, i.e. it has an autosar path
882        let new_ref = target.path()?;
883        // it must be possible to use the name of the referenced element name as an enum item in the dest attribute of the reference
884        let Some(enum_item) = EnumItem::from_str(target.element_name().to_str())
885            .ok()
886            .or(self.element_type().reference_dest_value(&target.element_type()))
887        else {
888            return Err(AutosarDataError::InvalidReference);
889        };
890
891        let model = self.model()?;
892        // build a target string, which is either the absolute path or the relative path depending on whether a base label was provided
893        let target_string = if let Some(base_label) = base_label {
894            // note - a reference can never occur outside a package, so we can unwrap Some(package) here without risking a panic
895            let ref_package_path = self.package()?.unwrap().path()?;
896            let base_path = model
897                .resolve_reference_base(base_label, &ref_package_path)
898                .ok_or(AutosarDataError::InvalidReferenceBase)?;
899            let mut trimmed_target_path = new_ref
900                .strip_prefix(&base_path)
901                .ok_or(AutosarDataError::InvalidReferenceBase)?;
902            if let Some(no_leading_slash) = trimmed_target_path.strip_prefix('/') {
903                trimmed_target_path = no_leading_slash;
904            }
905            trimmed_target_path.to_string()
906        } else {
907            new_ref
908        };
909
910        let version = self.min_version()?;
911        let mut element = self.0.write();
912        // set the DEST attribute first - this could fail if the target element has the wrong type
913        if element
914            .set_attribute_internal(AttributeName::Dest, CharacterData::Enum(enum_item), version)
915            .is_err()
916        {
917            return Err(AutosarDataError::InvalidReference);
918        }
919
920        let opt_old_ref = element.character_data().and_then(|cdata| cdata.string_value());
921
922        // if this reference previously referenced some other element, update
923        if let Some(old_ref) = opt_old_ref {
924            let old_base = element
925                .attribute_value(AttributeName::Base)
926                .and_then(|cdata| cdata.string_value());
927            model.fix_reference_origins(
928                &old_ref,
929                &target_string,
930                old_base.as_deref(),
931                base_label,
932                self.downgrade(),
933            );
934        } else {
935            // else initialise the new reference
936            model.add_reference_origin(&target_string, base_label, self.downgrade());
937        }
938
939        // if this is the PackageRef of a ReferenceBase, then we need to update the ReferenceBase index in the model
940        if element.element_name() == ElementName::PackageRef
941            && let Ok(Some(parent)) = element.parent()
942            && parent.element_name() == ElementName::ReferenceBase
943            && let Some(label) = parent
944                .get_sub_element(ElementName::ShortLabel)
945                .and_then(|e| e.character_data())
946                .and_then(|c| c.string_value())
947            && let Ok(Some(package)) = parent.package()
948            && let Ok(owner_package_path) = package.path()
949        {
950            model.fix_reference_base(
951                Some(label.clone()),
952                label,
953                target_string.clone(),
954                base_label.map(|s| s.to_string()),
955                owner_package_path,
956            );
957        }
958
959        // do the update
960        element.set_character_data(CharacterData::String(target_string), version)?;
961        if let Some(base_label) = base_label {
962            element.set_attribute_internal(
963                AttributeName::Base,
964                CharacterData::String(base_label.to_string()),
965                version,
966            )?;
967        } else {
968            // ensure BASE does not remain when retargeting from a relative reference to an absolute reference
969            element.remove_attribute(AttributeName::Base);
970        }
971
972        Ok(())
973    }
974
975    /// Get the referenced element
976    ///
977    /// This function will get the reference string from the character data of the element
978    /// as well as the destination type from the DEST attribute. Then a lookup of the Autosar
979    /// path is performed, and if an element is found at that path, then the type of the
980    /// element is compared to the expected type.
981    ///
982    /// The element is returned if it exists and its type is correct.
983    ///
984    /// # Example
985    ///
986    /// ```
987    /// # use autosar_data::*;
988    /// # fn main() -> Result<(), AutosarDataError> {
989    /// # let model = AutosarModel::new();
990    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
991    /// # let elements = model.root_element().create_sub_element(ElementName::ArPackages)
992    /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))
993    /// #   .and_then(|e| e.create_sub_element(ElementName::Elements))?;
994    /// # let ref_element = elements.create_named_sub_element(ElementName::System, "System")
995    /// #   .and_then(|e| e.create_sub_element(ElementName::FibexElements))
996    /// #   .and_then(|e| e.create_sub_element(ElementName::FibexElementRefConditional))
997    /// #   .and_then(|e| e.create_sub_element(ElementName::FibexElementRef))?;
998    /// let cluster_element = elements.create_named_sub_element(ElementName::CanCluster, "Cluster")?;
999    /// ref_element.set_reference_target(&cluster_element)?;
1000    /// let ref_target = ref_element.get_reference_target()?;
1001    /// assert_eq!(cluster_element, ref_target);
1002    /// # Ok(())
1003    /// # }
1004    /// ```
1005    ///
1006    /// # Errors
1007    ///
1008    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
1009    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
1010    ///    The operation was aborted to avoid a deadlock, but can be retried.
1011    ///  - [`AutosarDataError::NotReferenceElement`]: The current element is not a reference, so it is not possible to get the reference target
1012    ///  - [`AutosarDataError::InvalidReference`]: The reference is invalid; there is no element with the referenced Autosar path
1013    pub fn get_reference_target(&self) -> Result<Element, AutosarDataError> {
1014        if self.is_reference() {
1015            if let Some(CharacterData::String(reference)) = self.character_data() {
1016                let model = self.model()?;
1017
1018                let full_path = if let Some(base_label) = self
1019                    .attribute_value(AttributeName::Base)
1020                    .and_then(|cdata| cdata.string_value())
1021                {
1022                    // note - a reference can never occur outside a package, so we can unwrap Some(package) here without risking a panic
1023                    let ref_package_path = self.package()?.unwrap().path()?;
1024                    let reference_base = model
1025                        .resolve_reference_base(&base_label, &ref_package_path)
1026                        .ok_or(AutosarDataError::InvalidReference)?;
1027                    format!("{reference_base}/{reference}")
1028                } else {
1029                    reference
1030                };
1031                let target_elem = model
1032                    .get_element_by_path(&full_path)
1033                    .ok_or(AutosarDataError::InvalidReference)?;
1034
1035                let dest_value = self
1036                    .attribute_value(AttributeName::Dest)
1037                    .and_then(|cdata| cdata.enum_value())
1038                    .ok_or(AutosarDataError::InvalidReference)?;
1039                if target_elem.element_type().verify_reference_dest(dest_value) {
1040                    Ok(target_elem)
1041                } else {
1042                    Err(AutosarDataError::InvalidReference)
1043                }
1044            } else {
1045                Err(AutosarDataError::InvalidReference)
1046            }
1047        } else {
1048            Err(AutosarDataError::NotReferenceElement)
1049        }
1050    }
1051
1052    /// Set the character data of this element
1053    ///
1054    /// This method only applies to elements which contain character data, i.e. `element.content_type` == `CharacterData` or Mixed.
1055    /// On elements with mixed content this function will replace all current content with the single new `CharacterData` item.
1056    ///
1057    /// # Example
1058    ///
1059    /// ```
1060    /// # use autosar_data::*;
1061    /// # fn main() -> Result<(), AutosarDataError> {
1062    /// # let model = AutosarModel::new();
1063    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1064    /// # let element = model.root_element().create_sub_element(ElementName::ArPackages)
1065    /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))?
1066    /// #   .get_sub_element(ElementName::ShortName).unwrap();
1067    /// element.set_character_data("value")?;
1068    /// # Ok(())
1069    /// # }
1070    /// ```
1071    ///
1072    /// # Errors
1073    ///
1074    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
1075    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
1076    ///    The operation was aborted to avoid a deadlock, but can be retried.
1077    ///  - [`AutosarDataError::IncorrectContentType`]: Cannot set character data on an element which does not contain character data
1078    pub fn set_character_data<T: Into<CharacterData>>(&self, value: T) -> Result<(), AutosarDataError> {
1079        let chardata: CharacterData = value.into();
1080        self.set_character_data_internal(chardata)
1081    }
1082
1083    // internal function to set the character data - separated out since it doesn't need to be generic
1084    fn set_character_data_internal(&self, mut chardata: CharacterData) -> Result<(), AutosarDataError> {
1085        let elemtype = self.elemtype();
1086        if (elemtype.content_mode() == ContentMode::Characters || elemtype.content_mode() == ContentMode::Mixed)
1087            && let Some(cdata_spec) = elemtype.chardata_spec()
1088        {
1089            let model = self.model()?;
1090            let version = self.min_version()?;
1091            let mut compatible_value = CharacterData::check_value(&chardata, cdata_spec, version);
1092            if !compatible_value
1093                && matches!(
1094                    cdata_spec,
1095                    CharacterDataSpec::Pattern { .. } | CharacterDataSpec::String { .. }
1096                )
1097            {
1098                chardata = CharacterData::String(chardata.to_string());
1099                compatible_value = CharacterData::check_value(&chardata, cdata_spec, version);
1100            }
1101            if compatible_value {
1102                // if this is a SHORT-NAME element a whole lot of handling is needed in order to unbreak all the cross references
1103                let mut prev_path = None;
1104                if self.element_name() == ElementName::ShortName {
1105                    // this SHORT-NAME element might be newly created, in which case there is no previous path
1106                    if self.character_data().is_some()
1107                        && let Some(parent) = self.parent()?
1108                    {
1109                        prev_path = Some(parent.path()?);
1110                    }
1111                };
1112
1113                // if this is a reference, then some extra effort is needed there too
1114                let old_refval = if elemtype.is_ref() {
1115                    self.character_data().and_then(|cdata| cdata.string_value())
1116                } else {
1117                    None
1118                };
1119
1120                let old_label = if self.element_name() == ElementName::ShortLabel {
1121                    self.character_data().and_then(|cdata| cdata.string_value())
1122                } else {
1123                    None
1124                };
1125
1126                // update the character data
1127                {
1128                    let mut element = self.0.write();
1129                    element.content.clear();
1130                    element.content.push(ElementContent::CharacterData(chardata));
1131                }
1132
1133                // short-name: make sure the hashmap in the top-level AutosarModel is updated so that this element can still be found
1134                if let Some(prev_path) = prev_path
1135                    && let Some(parent) = self.parent()?
1136                {
1137                    let new_path = parent.path()?;
1138                    model.fix_identifiables(&prev_path, &new_path);
1139                }
1140
1141                // reference: update the references hashmap in the top-level AutosarModel
1142                if elemtype.is_ref()
1143                    && let Some(CharacterData::String(refval)) = self.character_data()
1144                {
1145                    let base = self
1146                        .attribute_value(AttributeName::Base)
1147                        .and_then(|cdata| cdata.string_value());
1148                    if let Some(old_refval) = old_refval {
1149                        model.fix_reference_origins(
1150                            &old_refval,
1151                            &refval,
1152                            base.as_deref(),
1153                            base.as_deref(),
1154                            self.downgrade(),
1155                        );
1156                    } else {
1157                        model.add_reference_origin(&refval, base.as_deref(), self.downgrade());
1158                    }
1159                } else if self.element_name() == ElementName::ShortLabel
1160                    && let Ok(Some(parent)) = self.parent()
1161                    && parent.element_name() == ElementName::ReferenceBase
1162                    && let Ok(Some(package)) = parent.package()
1163                    && let Ok(owner_package_path) = package.path()
1164                {
1165                    // setting or updating the label of a ReferenceBase
1166                    if let Some(package_ref) = parent.get_sub_element(ElementName::PackageRef)
1167                        && let Some(package_ref_val) =
1168                            package_ref.character_data().and_then(|cdata| cdata.string_value())
1169                    {
1170                        let base_attr = package_ref
1171                            .attribute_value(AttributeName::Base)
1172                            .and_then(|cdata| cdata.string_value());
1173                        // obviously the new label exists since we just set it
1174                        let new_label = self.character_data().and_then(|cdata| cdata.string_value()).unwrap();
1175                        model.fix_reference_base(old_label, new_label, package_ref_val, base_attr, owner_package_path);
1176                    }
1177                }
1178
1179                return Ok(());
1180            }
1181        }
1182        Err(AutosarDataError::IncorrectContentType {
1183            element: self.element_name(),
1184        })
1185    }
1186
1187    /// Remove the character data of this element
1188    ///
1189    /// This method only applies to elements which contain character data, i.e. `element.content_type` == `CharacterData`
1190    ///
1191    /// # Example
1192    ///
1193    /// ```
1194    /// # use autosar_data::*;
1195    /// # fn main() -> Result<(), AutosarDataError> {
1196    /// # let model = AutosarModel::new();
1197    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1198    /// # let element = model.root_element().create_sub_element(ElementName::ArPackages)
1199    /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))
1200    /// #   .and_then(|e| e.create_sub_element(ElementName::Elements))
1201    /// #   .and_then(|e| e.create_named_sub_element(ElementName::System, "System"))
1202    /// #   .and_then(|e| e. create_sub_element(ElementName::PncVectorLength))
1203    /// #   .unwrap();
1204    /// element.remove_character_data()?;
1205    /// # Ok(())
1206    /// # }
1207    /// ```
1208    ///
1209    /// # Errors
1210    ///
1211    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
1212    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
1213    ///    The operation was aborted to avoid a deadlock, but can be retried. Only relevant when removing references.
1214    ///  - [`AutosarDataError::ShortNameRemovalForbidden`]: Removing the character content of a SHORT-NAME is forbidden
1215    ///  - [`AutosarDataError::IncorrectContentType`]: Cannot set character data on an element whoch does not contain character data
1216    pub fn remove_character_data(&self) -> Result<(), AutosarDataError> {
1217        let elemtype = self.elemtype();
1218        if elemtype.content_mode() == ContentMode::Characters {
1219            if self.element_name() == ElementName::ShortName {
1220                Err(AutosarDataError::ShortNameRemovalForbidden)
1221            } else {
1222                if self.character_data().is_some() {
1223                    if self.is_reference() {
1224                        let model = self.model()?;
1225                        if let Some(CharacterData::String(reference)) = self.character_data() {
1226                            let base = self
1227                                .attribute_value(AttributeName::Base)
1228                                .and_then(|cdata| cdata.string_value());
1229                            model.remove_reference_origin(&reference, base.as_deref(), self.downgrade());
1230                        }
1231                    }
1232                    self.0.write().content.clear();
1233                }
1234                Ok(())
1235            }
1236        } else {
1237            Err(AutosarDataError::IncorrectContentType {
1238                element: self.element_name(),
1239            })
1240        }
1241    }
1242
1243    /// Insert a character data item into the content of this element
1244    ///
1245    /// This method only applies to elements which contain mixed data, i.e. `element.content_type`() == Mixed.
1246    /// Use `create_sub_element_at` to add an element instead of a character data item
1247    ///
1248    /// # Example
1249    ///
1250    /// ```
1251    /// # use autosar_data::*;
1252    /// # fn main() -> Result<(), AutosarDataError> {
1253    /// # let model = AutosarModel::new();
1254    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1255    /// # let element = model.root_element().create_sub_element(ElementName::ArPackages)
1256    /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))?;
1257    /// // mixed content elements are primarily used for documentation and description
1258    /// let desc = element.create_sub_element(ElementName::Desc)?;
1259    /// let l2 = desc.create_sub_element(ElementName::L2)?;
1260    /// l2.insert_character_content_item("descriptive text", 0)?;
1261    /// # Ok(())
1262    /// # }
1263    /// ```
1264    ///
1265    /// # Errors
1266    ///
1267    ///  - [`AutosarDataError::IncorrectContentType`] the element `content_type` is not Mixed
1268    ///  - [`AutosarDataError::InvalidPosition`] the position is not valid
1269    pub fn insert_character_content_item(&self, chardata: &str, position: usize) -> Result<(), AutosarDataError> {
1270        let mut element = self.0.write();
1271        if let ContentMode::Mixed = element.elemtype.content_mode() {
1272            if position <= element.content.len() {
1273                element.content.insert(
1274                    position,
1275                    ElementContent::CharacterData(CharacterData::String(chardata.to_owned())),
1276                );
1277                Ok(())
1278            } else {
1279                Err(AutosarDataError::InvalidPosition)
1280            }
1281        } else {
1282            Err(AutosarDataError::IncorrectContentType {
1283                element: element.element_name(),
1284            })
1285        }
1286    }
1287
1288    /// Remove a character data item from the content of this element
1289    ///
1290    /// This method only applies to elements which contain mixed data, i.e. `element.content_type` == Mixed
1291    ///
1292    /// # Example
1293    ///
1294    /// ```
1295    /// # use autosar_data::*;
1296    /// # fn main() -> Result<(), AutosarDataError> {
1297    /// # let model = AutosarModel::new();
1298    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1299    /// # let element = model.root_element().create_sub_element(ElementName::ArPackages)
1300    /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))
1301    /// #   .and_then(|e| e.create_sub_element(ElementName::Desc))
1302    /// #   .and_then(|e| e.create_sub_element(ElementName::L2))?;
1303    /// element.insert_character_content_item("descriptive text", 0)?;
1304    /// element.remove_character_content_item(0)?;
1305    /// # Ok(())
1306    /// # }
1307    /// ```
1308    ///
1309    /// # Errors
1310    ///
1311    ///  - [`AutosarDataError::IncorrectContentType`] the element `content_type` is not Mixed
1312    ///  - [`AutosarDataError::InvalidPosition`] the position is not valid
1313    pub fn remove_character_content_item(&self, position: usize) -> Result<(), AutosarDataError> {
1314        let mut element = self.0.write();
1315        if let ContentMode::Mixed = element.elemtype.content_mode() {
1316            if position < element.content.len()
1317                && let ElementContent::CharacterData(_) = element.content[position]
1318            {
1319                element.content.remove(position);
1320                return Ok(());
1321            }
1322            Err(AutosarDataError::InvalidPosition)
1323        } else {
1324            Err(AutosarDataError::IncorrectContentType {
1325                element: element.element_name(),
1326            })
1327        }
1328    }
1329
1330    /// returns the number of content items in this element
1331    /// ```
1332    /// # use autosar_data::*;
1333    /// # fn main() -> Result<(), AutosarDataError> {
1334    /// # let model = AutosarModel::new();
1335    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1336    /// # let pkg = model.root_element().create_sub_element(ElementName::ArPackages)
1337    /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))?;
1338    /// assert_eq!(pkg.content_item_count(), 1);
1339    /// # Ok(())
1340    /// # }
1341    /// ```
1342    #[must_use]
1343    pub fn content_item_count(&self) -> usize {
1344        self.0.read().content.len()
1345    }
1346
1347    /// Get the character content of the element
1348    ///
1349    /// This method only applies to elements which contain character data, i.e. `element.content_type`() == `CharacterData`
1350    ///
1351    /// # Example
1352    ///
1353    /// ```
1354    /// # use autosar_data::*;
1355    /// # fn main() -> Result<(), AutosarDataError> {
1356    /// # let model = AutosarModel::new();
1357    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1358    /// # let element = model.root_element().create_sub_element(ElementName::ArPackages)
1359    /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))?
1360    /// #   .get_sub_element(ElementName::ShortName).unwrap();
1361    /// match element.character_data() {
1362    ///     Some(CharacterData::String(stringval)) => {},
1363    ///     Some(CharacterData::Enum(enumval)) => {},
1364    ///     Some(CharacterData::UnsignedInteger(intval)) => {},
1365    ///     Some(CharacterData::Float(floatval)) => {},
1366    ///     None => {},
1367    /// }
1368    /// # Ok(())
1369    /// # }
1370    /// ```
1371    #[must_use]
1372    pub fn character_data(&self) -> Option<CharacterData> {
1373        self.0.read().character_data()
1374    }
1375
1376    /// Create an iterator over all of the content of this element
1377    ///
1378    /// The iterator can return both sub elements and character data, wrapped as `ElementContent::Element` and `ElementContent::CharacterData`
1379    ///
1380    /// This method is intended to be used with elements that contain mixed content.
1381    ///
1382    /// # Example
1383    ///
1384    /// ```
1385    /// # use autosar_data::*;
1386    /// # let model = AutosarModel::new();
1387    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1388    /// # let element = model.root_element();
1389    /// for content_item in element.content() {
1390    ///     match content_item {
1391    ///         ElementContent::CharacterData(data) => {},
1392    ///         ElementContent::Element(element) => {},
1393    ///     }
1394    /// }
1395    /// ```
1396    #[must_use]
1397    pub fn content(&self) -> ElementContentIterator {
1398        ElementContentIterator::new(self)
1399    }
1400
1401    /// Create a weak reference to this element
1402    ///
1403    /// A weak reference can be stored without preventing the element from being deallocated.
1404    /// The weak reference has to be upgraded in order to be used, which can fail if the element no longer exists.
1405    ///
1406    /// See the documentation for [Arc]
1407    ///
1408    /// # Example
1409    ///
1410    /// ```
1411    /// # use autosar_data::*;
1412    /// # let model = AutosarModel::new();
1413    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1414    /// # let element = model.root_element();
1415    /// let weak_element = element.downgrade();
1416    /// ```
1417    #[must_use]
1418    pub fn downgrade(&self) -> WeakElement {
1419        WeakElement(Arc::downgrade(&self.0))
1420    }
1421
1422    /// return the position of this element within the parent element
1423    ///
1424    /// None may be returned if the element has been deleted, or for the root element (AUTOSAR) which has no parent.
1425    /// The returned position can be used with `get_sub_element_at()`.
1426    ///
1427    /// # Example
1428    ///
1429    /// ```
1430    /// # use autosar_data::*;
1431    /// # let model = AutosarModel::new();
1432    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1433    /// # let el_ar_packages = model.root_element().create_sub_element(ElementName::ArPackages).unwrap();
1434    /// let el_pkg1 = el_ar_packages.create_named_sub_element(ElementName::ArPackage, "Pkg1").unwrap();
1435    /// let el_pkg2 = el_ar_packages.create_named_sub_element(ElementName::ArPackage, "Pkg2").unwrap();
1436    /// let el_pkg3 = el_ar_packages.create_named_sub_element(ElementName::ArPackage, "Pkg3").unwrap();
1437    /// let position = el_pkg2.position().unwrap();
1438    /// assert_eq!(position, 1);
1439    /// assert_eq!(el_pkg2, el_ar_packages.get_sub_element_at(position).unwrap());
1440    /// ```
1441    #[must_use]
1442    pub fn position(&self) -> Option<usize> {
1443        if let Ok(Some(parent)) = self.parent() {
1444            parent
1445                .0
1446                .read()
1447                .content
1448                .iter()
1449                .position(|ec| matches!(ec, ElementContent::Element(elem) if elem == self))
1450        } else {
1451            None
1452        }
1453    }
1454
1455    /// Create an iterator over all sub elements of this element
1456    ///
1457    /// # Example
1458    ///
1459    /// ```
1460    /// # use autosar_data::*;
1461    /// # let model = AutosarModel::new();
1462    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1463    /// # let element = model.root_element();
1464    /// for sub_element in element.sub_elements() {
1465    ///     // ...
1466    /// }
1467    /// ```
1468    #[must_use]
1469    pub fn sub_elements(&self) -> ElementsIterator {
1470        ElementsIterator::new(self.clone())
1471    }
1472
1473    /// Get the sub element with the given element name
1474    ///
1475    /// Returns None if no such element exists. if there are multiple sub elements with the requested name, then only the first is returned
1476    ///
1477    /// # Example
1478    ///
1479    /// ```
1480    /// # use autosar_data::*;
1481    /// # fn main() -> Result<(), AutosarDataError> {
1482    /// # let model = AutosarModel::new();
1483    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1484    /// # let pkg = model.root_element().create_sub_element(ElementName::ArPackages)
1485    /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))?;
1486    /// let element = pkg.get_sub_element(ElementName::ShortName).unwrap();
1487    /// assert_eq!(element.element_name(), ElementName::ShortName);
1488    /// # Ok(())
1489    /// # }
1490    /// ```
1491    #[must_use]
1492    pub fn get_sub_element(&self, name: ElementName) -> Option<Element> {
1493        let locked_elem = self.0.read();
1494        for item in &locked_elem.content {
1495            if let ElementContent::Element(subelem) = item
1496                && subelem.element_name() == name
1497            {
1498                return Some(subelem.clone());
1499            }
1500        }
1501        None
1502    }
1503
1504    /// Get the sub element at the given position.
1505    ///
1506    /// Returns None if no such element exists.
1507    ///
1508    /// # Example
1509    ///
1510    /// ```
1511    /// # use autosar_data::*;
1512    /// # fn main() -> Result<(), AutosarDataError> {
1513    /// # let model = AutosarModel::new();
1514    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1515    /// # let pkg = model.root_element().create_sub_element(ElementName::ArPackages)
1516    /// #   .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))?;
1517    /// let element = pkg.get_sub_element_at(0).unwrap();
1518    /// assert_eq!(element.element_name(), ElementName::ShortName);
1519    /// # Ok(())
1520    /// # }
1521    /// ```
1522    #[must_use]
1523    pub fn get_sub_element_at(&self, position: usize) -> Option<Element> {
1524        let locked_elem = self.0.read();
1525        if let Some(ElementContent::Element(subelem)) = locked_elem.content.get(position) {
1526            return Some(subelem.clone());
1527        }
1528        None
1529    }
1530
1531    /// Get or create a sub element
1532    ///
1533    /// This is a shorthand for `get_sub_element` followed by `create_cub_element` if getting an existing element fails.
1534    ///
1535    /// # Example
1536    ///
1537    /// ```
1538    /// # use autosar_data::*;
1539    /// # fn main() -> Result<(), AutosarDataError> {
1540    /// # let model = AutosarModel::new();
1541    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1542    /// let element = model.root_element().get_or_create_sub_element(ElementName::ArPackages)?;
1543    /// let element2 = model.root_element().get_or_create_sub_element(ElementName::ArPackages)?;
1544    /// assert_eq!(element, element2);
1545    /// # Ok(())
1546    /// # }
1547    /// ```
1548    ///
1549    /// # Errors
1550    ///
1551    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
1552    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
1553    ///    The operation was aborted to avoid a deadlock, but can be retried.
1554    ///  - [`AutosarDataError::IncorrectContentType`]: A sub element may not be created in an element with content type `CharacterData`.
1555    ///  - [`AutosarDataError::ElementInsertionConflict`]: The requested sub element cannot be created because it conflicts with an existing sub element.
1556    ///  - [`AutosarDataError::InvalidSubElement`]: The `ElementName` is not a valid sub element according to the specification.
1557    ///  - [`AutosarDataError::ItemNameRequired`]: The sub element requires an item name, so you must use `create_named_sub_element`().
1558    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
1559    pub fn get_or_create_sub_element(&self, name: ElementName) -> Result<Element, AutosarDataError> {
1560        let version = self.min_version()?;
1561        let mut locked_elem = self.0.try_write().ok_or(AutosarDataError::ParentElementLocked)?;
1562        for item in &locked_elem.content {
1563            if let ElementContent::Element(subelem) = item
1564                && subelem.element_name() == name
1565            {
1566                return Ok(subelem.clone());
1567            }
1568        }
1569        locked_elem.create_sub_element(self.downgrade(), name, version)
1570    }
1571    /// Get or create a named sub element
1572    ///
1573    /// Checks if a matching subelement exists, and returns it if it does.
1574    /// If no matching subelement exists, tries to create one.
1575    ///
1576    /// # Example
1577    ///
1578    /// ```
1579    /// # use autosar_data::*;
1580    /// # fn main() -> Result<(), AutosarDataError> {
1581    /// # let model = AutosarModel::new();
1582    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1583    /// let ar_packages = model.root_element().get_or_create_sub_element(ElementName::ArPackages)?;
1584    /// let pkg = ar_packages.get_or_create_named_sub_element(ElementName::ArPackage, "Pkg")?;
1585    /// let pkg_2 = ar_packages.get_or_create_named_sub_element(ElementName::ArPackage, "Pkg")?;
1586    /// assert_eq!(pkg, pkg_2);
1587    /// # Ok(())
1588    /// # }
1589    /// ```
1590    ///
1591    /// # Errors
1592    ///
1593    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
1594    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
1595    ///    The operation was aborted to avoid a deadlock, but can be retried.
1596    ///  - [`AutosarDataError::IncorrectContentType`]: A sub element may not be created in an element with content type `CharacterData`.
1597    ///  - [`AutosarDataError::ElementInsertionConflict`]: The requested sub element cannot be created because it conflicts with an existing sub element.
1598    ///  - [`AutosarDataError::InvalidSubElement`]: The `ElementName` is not a valid sub element according to the specification.
1599    ///  - [`AutosarDataError::ItemNameRequired`]: The sub element requires an item name, so you must use `create_named_sub_element`().
1600    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
1601    pub fn get_or_create_named_sub_element(
1602        &self,
1603        element_name: ElementName,
1604        item_name: &str,
1605    ) -> Result<Element, AutosarDataError> {
1606        let model = self.model()?;
1607        let version = self.min_version()?;
1608        let mut locked_elem = self.0.try_write().ok_or(AutosarDataError::ParentElementLocked)?;
1609        for item in &locked_elem.content {
1610            if let ElementContent::Element(subelem) = item
1611                && subelem.element_name() == element_name
1612                && subelem.item_name().as_deref().unwrap_or("") == item_name
1613            {
1614                return Ok(subelem.clone());
1615            }
1616        }
1617        locked_elem.create_named_sub_element(self.downgrade(), element_name, item_name, &model, version)
1618    }
1619
1620    /// Create a depth first iterator over this element and all of its sub elements
1621    ///
1622    /// Each step in the iteration returns the depth and an element. Due to the nature of a depth first search,
1623    /// the returned depth can remain the same, increase by one, or decrease by an arbitrary number in each step.
1624    ///
1625    /// The dfs iterator will always return this element as the first item.
1626    ///
1627    /// # Example
1628    ///
1629    /// ```
1630    /// # use autosar_data::*;
1631    /// # let model = AutosarModel::new();
1632    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1633    /// # let element = model.root_element();
1634    /// for (depth, elem) in element.elements_dfs() {
1635    ///     // ...
1636    /// }
1637    /// ```
1638    #[must_use]
1639    pub fn elements_dfs(&self) -> ElementsDfsIterator {
1640        ElementsDfsIterator::new(self, 0)
1641    }
1642
1643    /// Create a depth first iterator over this element and all of its sub elements up to a maximum depth
1644    ///
1645    /// Each step in the iteration returns the depth and an element. Due to the nature of a depth first search,
1646    /// the returned depth can remain the same, increase by one, or decrease by an arbitrary number in each step.
1647    ///
1648    /// The dfs iterator will always return this element as the first item. A `max_depth` of `0` returns all
1649    /// child elements, regardless of depth (like `elements_dfs` does).
1650    ///
1651    /// # Example
1652    ///
1653    /// ```
1654    /// # use autosar_data::*;
1655    /// # let model = AutosarModel::new();
1656    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1657    /// # let element = model.root_element();
1658    /// # element.create_sub_element(ElementName::ArPackages).unwrap();
1659    /// # let sub_elem = element.get_sub_element(ElementName::ArPackages).unwrap();
1660    /// # sub_elem.create_named_sub_element(ElementName::ArPackage, "test2").unwrap();
1661    /// for (depth, elem) in element.elements_dfs_with_max_depth(1) {
1662    ///     assert!(depth <= 1);
1663    ///     // ...
1664    /// }
1665    /// ```
1666    #[must_use]
1667    pub fn elements_dfs_with_max_depth(&self, max_depth: usize) -> ElementsDfsIterator {
1668        ElementsDfsIterator::new(self, max_depth)
1669    }
1670
1671    /// Create an iterator over all the attributes in this element
1672    ///
1673    /// # Example
1674    ///
1675    /// ```
1676    /// # use autosar_data::*;
1677    /// # let model = AutosarModel::new();
1678    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1679    /// # let element = model.root_element();
1680    /// for attribute in element.attributes() {
1681    ///     println!("{} = {}", attribute.attrname, attribute.content);
1682    /// }
1683    /// ```
1684    #[must_use]
1685    pub fn attributes(&self) -> AttributeIterator {
1686        AttributeIterator {
1687            element: self.clone(),
1688            index: 0,
1689        }
1690    }
1691
1692    /// Get the value of an attribute by name
1693    ///
1694    /// # Example
1695    ///
1696    /// ```
1697    /// # use autosar_data::*;
1698    /// # let model = AutosarModel::new();
1699    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1700    /// let value = model.root_element().attribute_value(AttributeName::xsiSchemalocation);
1701    /// ```
1702    #[must_use]
1703    pub fn attribute_value(&self, attrname: AttributeName) -> Option<CharacterData> {
1704        self.0.read().attribute_value(attrname)
1705    }
1706
1707    /// Set the value of a named attribute
1708    ///
1709    /// If no attribute by that name exists, and the attribute is a valid attribute of the element, then the attribute will be created.
1710    ///
1711    /// Returns Ok(()) if the attribute was set, otherwise the Err indicates why setting the attribute failed.
1712    ///
1713    /// ```
1714    /// # use autosar_data::*;
1715    /// # let model = AutosarModel::new();
1716    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1717    /// # let element = model.root_element();
1718    /// let result = element.set_attribute(AttributeName::S, CharacterData::String("1234-5678".to_string()));
1719    /// # assert!(result.is_ok());
1720    /// ```
1721    ///
1722    /// # Errors
1723    ///
1724    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
1725    ///  - [`AutosarDataError::InvalidAttribute`]: The `AttributeName` is not valid for this element
1726    ///  - [`AutosarDataError::InvalidAttributeValue`]: The value is not valid for this attribute in this element
1727    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
1728    pub fn set_attribute<T: Into<CharacterData>>(
1729        &self,
1730        attrname: AttributeName,
1731        value: T,
1732    ) -> Result<(), AutosarDataError> {
1733        let version = self.min_version()?;
1734        let (old_base, reference_value) = self.base_attribute_info(attrname);
1735
1736        self.0.write().set_attribute_internal(attrname, value.into(), version)?;
1737
1738        self.base_attribute_fixup(attrname, old_base, reference_value);
1739
1740        Ok(())
1741    }
1742
1743    /// Set the value of a named attribute from a string
1744    ///
1745    /// The function tries to convert the string to the correct data type for the attribute
1746    ///
1747    /// Returns Ok(()) if the attribute was set, otherwise the Err indicates why setting the attribute failed.
1748    ///
1749    /// ```
1750    /// # use autosar_data::*;
1751    /// # let model = AutosarModel::new();
1752    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1753    /// # let element = model.root_element();
1754    /// let result = element.set_attribute_string(AttributeName::T, "2022-01-31T13:59:59Z");
1755    /// # assert!(result.is_ok());
1756    /// ```
1757    ///
1758    /// # Errors
1759    ///
1760    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
1761    ///  - [`AutosarDataError::InvalidAttribute`]: The `AttributeName` is not valid for this element
1762    ///  - [`AutosarDataError::InvalidAttributeValue`]: The value is not valid for this attribute in this element
1763    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
1764    pub fn set_attribute_string(&self, attrname: AttributeName, stringvalue: &str) -> Result<(), AutosarDataError> {
1765        let version = self.min_version()?;
1766        let (old_base, reference_value) = self.base_attribute_info(attrname);
1767
1768        self.0.write().set_attribute_string(attrname, stringvalue, version)?;
1769
1770        // post-change fixup for BASE attribute changes only
1771        self.base_attribute_fixup(attrname, old_base, reference_value);
1772
1773        Ok(())
1774    }
1775
1776    /// Remove an attribute from the element
1777    ///
1778    /// Returns true if the attribute existed and could be removed.
1779    ///
1780    /// # Example
1781    ///
1782    /// ```
1783    /// # use autosar_data::*;
1784    /// # let model = AutosarModel::new();
1785    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1786    /// let result = model.root_element().remove_attribute(AttributeName::xsiSchemalocation);
1787    /// // xsiSchemalocation exists in the AUTOSAR element, but it is mandatory and cannot be removed
1788    /// assert_eq!(result, false);
1789    /// ```
1790    #[must_use]
1791    pub fn remove_attribute(&self, attrname: AttributeName) -> bool {
1792        let (old_base, reference_value) = self.base_attribute_info(attrname);
1793
1794        let removed = self.0.write().remove_attribute(attrname);
1795
1796        if removed {
1797            // fix the cache of reference origins if a BASE attribute was removed
1798            self.base_attribute_fixup(attrname, old_base, reference_value);
1799        }
1800        removed
1801    }
1802
1803    /// helper function to get the old BASE attribute value and the reference value before changing or removing the attribute
1804    fn base_attribute_info(&self, attrname: AttributeName) -> (Option<String>, Option<String>) {
1805        if !self.is_reference() || attrname != AttributeName::Base {
1806            return (None, None);
1807        }
1808        let old_base = self
1809            .attribute_value(AttributeName::Base)
1810            .and_then(|cdata| cdata.string_value());
1811        let reference_value = self.character_data().and_then(|cdata| cdata.string_value());
1812        (old_base, reference_value)
1813    }
1814
1815    /// fix the reference origins of a reference element if the BASE attribute was changed or removed
1816    fn base_attribute_fixup(&self, attrname: AttributeName, old_base: Option<String>, reference_value: Option<String>) {
1817        if attrname == AttributeName::Base
1818            && let Some(reference_value) = reference_value
1819            && let Ok(model) = self.model()
1820        {
1821            let new_base = self
1822                .attribute_value(AttributeName::Base)
1823                .and_then(|cdata| cdata.string_value());
1824            model.fix_reference_origins(
1825                &reference_value,
1826                &reference_value,
1827                old_base.as_deref(),
1828                new_base.as_deref(),
1829                self.downgrade(),
1830            );
1831        }
1832    }
1833
1834    /// Recursively sort all sub-elements of this element
1835    ///
1836    /// All sub elements of the current element are sorted alphabetically.
1837    /// If the sub-elements are named, then the sorting is performed according to the item names,
1838    /// otherwise the serialized form of the sub-elements is used for sorting.
1839    /// Element attributes are not taken into account while sorting.
1840    /// The elements are sorted in place, and sorting cannot fail, so there is no return value.
1841    ///
1842    /// # Example
1843    /// ```
1844    /// # use autosar_data::*;
1845    /// # let model = AutosarModel::new();
1846    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1847    /// # let element = model.root_element();
1848    /// element.sort();
1849    /// ```
1850    pub fn sort(&self) {
1851        self.0.write().sort();
1852    }
1853
1854    /// Serialize the element and all of its content to a string
1855    ///
1856    /// The serialized text generated for elements below the root element cannot be loaded, but it may be useful for display.
1857    ///
1858    /// # Example
1859    ///
1860    /// ```
1861    /// # use autosar_data::*;
1862    /// # let model = AutosarModel::new();
1863    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
1864    /// # let element = model.root_element();
1865    /// let text = element.serialize();
1866    /// ```
1867    #[must_use]
1868    pub fn serialize(&self) -> String {
1869        let mut outstring = String::new();
1870
1871        self.serialize_internal(&mut outstring, 0, false, &None);
1872
1873        outstring
1874    }
1875
1876    pub(crate) fn serialize_internal(
1877        &self,
1878        outstring: &mut String,
1879        indent: usize,
1880        inline: bool,
1881        for_file: &Option<WeakArxmlFile>,
1882    ) {
1883        let element = self.0.read();
1884        let element_name = element.elemname.to_str();
1885
1886        if let Some(comment) = &self.0.read().comment {
1887            // put the comment on a separate line
1888            if !inline {
1889                Self::serialize_newline_indent(outstring, indent);
1890            }
1891            outstring.push_str("<!--");
1892            outstring.push_str(comment);
1893            outstring.push_str("-->");
1894        }
1895
1896        // write the opening tag on a new line and indent it
1897        if !inline {
1898            Self::serialize_newline_indent(outstring, indent);
1899        }
1900
1901        if !element.content.is_empty() {
1902            outstring.push('<');
1903            outstring.push_str(element_name);
1904            self.serialize_attributes(outstring);
1905            outstring.push('>');
1906
1907            match self.content_type() {
1908                ContentType::Elements => {
1909                    // serialize each sub-element
1910                    for subelem in self.sub_elements() {
1911                        if for_file.is_none()
1912                            || subelem.0.read().file_membership.is_empty()
1913                            || subelem.0.read().file_membership.contains(for_file.as_ref().unwrap())
1914                        {
1915                            subelem.serialize_internal(outstring, indent + 1, false, for_file);
1916                        }
1917                    }
1918                    // put the closing tag on a new line and indent it
1919                    Self::serialize_newline_indent(outstring, indent);
1920                    outstring.push_str("</");
1921                    outstring.push_str(element_name);
1922                    outstring.push('>');
1923                }
1924                ContentType::CharacterData => {
1925                    // write the character data on the same line as the opening tag
1926                    if let Some(ElementContent::CharacterData(chardata)) = element.content.first() {
1927                        chardata.serialize_internal(outstring);
1928                    }
1929
1930                    // write the closing tag on the same line
1931                    outstring.push_str("</");
1932                    outstring.push_str(element_name);
1933                    outstring.push('>');
1934                }
1935                ContentType::Mixed => {
1936                    for item in self.content() {
1937                        match item {
1938                            ElementContent::Element(subelem) => {
1939                                if for_file.is_none()
1940                                    || subelem.0.read().file_membership.is_empty()
1941                                    || subelem.0.read().file_membership.contains(for_file.as_ref().unwrap())
1942                                {
1943                                    subelem.serialize_internal(outstring, indent + 1, true, for_file);
1944                                }
1945                            }
1946                            ElementContent::CharacterData(chardata) => {
1947                                chardata.serialize_internal(outstring);
1948                            }
1949                        }
1950                    }
1951                    // write the closing tag on the same line
1952                    outstring.push_str("</");
1953                    outstring.push_str(element_name);
1954                    outstring.push('>');
1955                }
1956            }
1957        } else {
1958            outstring.push('<');
1959            outstring.push_str(element_name);
1960            self.serialize_attributes(outstring);
1961            outstring.push('/');
1962            outstring.push('>');
1963        }
1964    }
1965
1966    fn serialize_newline_indent(outstring: &mut String, indent: usize) {
1967        outstring.push('\n');
1968        for _ in 0..indent {
1969            outstring.push_str("  ");
1970        }
1971    }
1972
1973    fn serialize_attributes(&self, outstring: &mut String) {
1974        let element = self.0.read();
1975        if !element.attributes.is_empty() {
1976            for attribute in &element.attributes {
1977                outstring.push(' ');
1978                outstring.push_str(attribute.attrname.to_str());
1979                outstring.push_str("=\"");
1980                attribute.content.serialize_internal(outstring);
1981                outstring.push('"');
1982            }
1983        }
1984    }
1985
1986    pub(crate) fn elemtype(&self) -> ElementType {
1987        self.0.read().elemtype
1988    }
1989
1990    // an element might have a diffeent element type depending on the version - as a result of a
1991    // changed datatype of the CharacterData, or because the element ordering was changed
1992    fn recalc_element_type(&self, target_version: AutosarVersion) -> ElementType {
1993        if let Ok(Some(parent)) = self.parent()
1994            && let Some((etype, ..)) = parent
1995                .element_type()
1996                .find_sub_element(self.element_name(), target_version as u32)
1997        {
1998            return etype;
1999        }
2000
2001        self.element_type()
2002    }
2003
2004    /// check if the sub elements and attributes of this element are compatible with some `target_version`
2005    pub(crate) fn check_version_compatibility(
2006        &self,
2007        file: &WeakArxmlFile,
2008        target_version: AutosarVersion,
2009    ) -> (Vec<CompatibilityError>, u32) {
2010        let mut compat_errors = Vec::new();
2011        let mut overall_version_mask = u32::MAX;
2012
2013        // make sure compatibility checks are performed with the element type used in the target version
2014        let elemtype_new = self.recalc_element_type(target_version);
2015
2016        // check the compatibility of all the attributes in this element
2017        {
2018            let element = self.0.read();
2019            for attribute in &element.attributes {
2020                // find the specification for the current attribute
2021                if let Some(AttributeSpec {
2022                    spec: value_spec,
2023                    version: version_mask,
2024                    ..
2025                }) = elemtype_new.find_attribute_spec(attribute.attrname)
2026                {
2027                    overall_version_mask &= version_mask;
2028                    // check if the attribute is allowed at all
2029                    if !target_version.compatible(version_mask) {
2030                        compat_errors.push(CompatibilityError::IncompatibleAttribute {
2031                            element: self.clone(),
2032                            attribute: attribute.attrname,
2033                            version_mask,
2034                        });
2035                    } else {
2036                        let (is_compatible, value_version_mask) = attribute
2037                            .content
2038                            .check_version_compatibility(value_spec, target_version);
2039                        if !is_compatible {
2040                            compat_errors.push(CompatibilityError::IncompatibleAttributeValue {
2041                                element: self.clone(),
2042                                attribute: attribute.attrname,
2043                                attribute_value: attribute.content.to_string(),
2044                                version_mask: value_version_mask,
2045                            });
2046                        }
2047                        overall_version_mask &= value_version_mask;
2048                    }
2049                }
2050            }
2051        }
2052
2053        // check the compatibility of all sub-elements
2054        for sub_element in self.sub_elements() {
2055            if (sub_element.0.read().file_membership.is_empty() || sub_element.0.read().file_membership.contains(file))
2056                && let Some((_, indices)) = elemtype_new
2057                    .find_sub_element(sub_element.element_name(), target_version as u32)
2058                    .or(elemtype_new.find_sub_element(sub_element.element_name(), u32::MAX))
2059            {
2060                let version_mask = self.element_type().get_sub_element_version_mask(&indices).unwrap();
2061                overall_version_mask &= version_mask;
2062                if !target_version.compatible(version_mask) {
2063                    compat_errors.push(CompatibilityError::IncompatibleElement {
2064                        element: sub_element.clone(),
2065                        version_mask,
2066                    });
2067                } else {
2068                    let (mut sub_element_errors, sub_element_mask) =
2069                        sub_element.check_version_compatibility(file, target_version);
2070                    compat_errors.append(&mut sub_element_errors);
2071                    overall_version_mask &= sub_element_mask;
2072                }
2073            }
2074        }
2075
2076        (compat_errors, overall_version_mask)
2077    }
2078
2079    /// List all `sub_elements` that are valid in the current element
2080    ///
2081    /// The target use case is direct interaction with a user, e.g. through a selection dialog
2082    ///
2083    /// # Return Value
2084    ///
2085    /// A list of tuples consisting of
2086    ///     `ElementName` of the potential sub element
2087    ///     bool: is the sub element named
2088    ///     bool: can this sub element be inserted considering the current content of the element
2089    ///
2090    /// # Example
2091    ///
2092    /// ```
2093    /// # use autosar_data::*;
2094    /// # let model = AutosarModel::new();
2095    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
2096    /// # let element = model.root_element();
2097    /// for ValidSubElementInfo{element_name, is_named, is_allowed} in element.list_valid_sub_elements() {
2098    ///     // ...
2099    /// }
2100    /// ```
2101    #[must_use]
2102    pub fn list_valid_sub_elements(&self) -> Vec<ValidSubElementInfo> {
2103        let etype = self.0.read().elemtype;
2104        let mut valid_sub_elements = Vec::new();
2105
2106        if let Ok(version) = self.min_version() {
2107            for (element_name, _, version_mask, named_mask) in etype.sub_element_spec_iter() {
2108                if version.compatible(version_mask) {
2109                    let is_named = version.compatible(named_mask);
2110                    let is_allowed = self.0.read().calc_element_insert_range(element_name, version).is_ok();
2111                    valid_sub_elements.push(ValidSubElementInfo {
2112                        element_name,
2113                        is_named,
2114                        is_allowed,
2115                    });
2116                }
2117            }
2118        }
2119
2120        valid_sub_elements
2121    }
2122
2123    /// Return the set of files in which the current element is present
2124    ///
2125    /// # Return Value
2126    ///
2127    /// 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.
2128    ///
2129    /// # Example
2130    ///
2131    /// ```
2132    /// # use autosar_data::*;
2133    /// # fn main() -> Result<(), AutosarDataError> {
2134    /// # let model = AutosarModel::new();
2135    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
2136    /// # let element = model.root_element();
2137    /// let (inherited, file_membership) = element.file_membership()?;
2138    /// # Ok(())
2139    /// # }
2140    /// ```
2141    /// # Errors
2142    ///
2143    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
2144    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
2145    ///    The operation was aborted to avoid a deadlock, but can be retried.
2146    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
2147    pub fn file_membership(&self) -> Result<(bool, HashSet<WeakArxmlFile>), AutosarDataError> {
2148        let mut cur_elem_opt = Some(self.clone());
2149        while let Some(cur_elem) = &cur_elem_opt {
2150            let locked_cur_elem = cur_elem
2151                .0
2152                .try_read_for(std::time::Duration::from_millis(10))
2153                .ok_or(AutosarDataError::ParentElementLocked)?;
2154            if !locked_cur_elem.file_membership.is_empty() {
2155                return Ok((cur_elem == self, locked_cur_elem.file_membership.clone()));
2156            }
2157            drop(locked_cur_elem);
2158
2159            cur_elem_opt = cur_elem.parent()?;
2160        }
2161
2162        // no file membership info found at any level - this only happens if the model does not contain any files
2163        Err(AutosarDataError::NoFilesInModel)
2164    }
2165
2166    /// return the file membership of this element without trying to get an inherited value
2167    pub(crate) fn file_membership_local(&self) -> HashSet<WeakArxmlFile> {
2168        self.0.read().file_membership.clone()
2169    }
2170
2171    /// set the file membership of an element
2172    ///
2173    /// The passed set acts as a restriction of the file membership of the parent element.
2174    /// This means that the set of a child cannot be greater than that of the parent.
2175    ///
2176    /// Setting an empty set has a special meaning: it reverts the membership to default,
2177    /// i.e. inherited from the parent with no additional restriction
2178    pub(crate) fn set_file_membership(&self, file_membership: HashSet<WeakArxmlFile>) {
2179        // find out if the parent is splittable. If the parent is unavaliable, assume
2180        // that the caller knows what they're doing and assume it is splittable
2181        let parent_splittable = self
2182            .parent()
2183            .ok()
2184            .flatten()
2185            .map_or(u32::MAX, |p| p.element_type().splittable());
2186        // can always reset the membership to empty = inherited; otherwise the parent must be splittable
2187        if file_membership.is_empty() || parent_splittable != 0 {
2188            self.0.write().file_membership = file_membership;
2189        }
2190    }
2191
2192    /// add the current element to the given file
2193    ///
2194    /// In order to successfully cause the element to appear in the serialized file data, all parent elements
2195    /// of the current element will also be added if required.
2196    ///
2197    /// If the model only has a single file then this function does nothing.
2198    ///
2199    /// # Example
2200    /// ```
2201    /// # use autosar_data::*;
2202    /// # use std::collections::HashSet;
2203    /// # let model = AutosarModel::new();
2204    /// let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
2205    /// # let element = model.root_element();
2206    /// element.add_to_file(&file);
2207    /// ```
2208    ///
2209    /// # Errors
2210    ///
2211    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
2212    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
2213    ///    The operation was aborted to avoid a deadlock, but can be retried.
2214    ///
2215    pub fn add_to_file(&self, file: &ArxmlFile) -> Result<(), AutosarDataError> {
2216        let parent_splittable = self.parent()?.is_none_or(|p| p.element_type().splittable() != 0);
2217        if parent_splittable {
2218            if file.model()? == self.model()? {
2219                let weak_file = file.downgrade();
2220                // current_fileset is the set of files which contain the current element
2221                let (_, current_fileset) = self.file_membership()?;
2222                // if the model only has a single file or if the element is already in the set then there is nothing to do
2223                if !current_fileset.contains(&weak_file) {
2224                    let mut updated_fileset = current_fileset;
2225                    updated_fileset.insert(weak_file);
2226                    self.0.write().file_membership = updated_fileset;
2227
2228                    // recursively continue with the parent
2229                    if let Some(parent) = self.parent()? {
2230                        parent.add_to_file_restricted(file)?;
2231                    }
2232                }
2233                Ok(())
2234            } else {
2235                // adding a file from a different model is not permitted
2236                Err(AutosarDataError::InvalidFile)
2237            }
2238        } else {
2239            Err(AutosarDataError::FilesetModificationForbidden)
2240        }
2241    }
2242
2243    /// add only this element and its direct parents to a file, but not its children
2244    pub(crate) fn add_to_file_restricted(&self, file: &ArxmlFile) -> Result<(), AutosarDataError> {
2245        let weak_file = file.downgrade();
2246        let (local, current_fileset) = self.file_membership().unwrap_or((true, HashSet::new()));
2247
2248        if !current_fileset.contains(&weak_file) {
2249            // if the current element is splittable, then all of its subelements are allowed to have their own filesets
2250            // unless something else is already set, they should get the current unmodified file membership of this element
2251            // which does not include the new file
2252            if self.element_type().splittable() != 0 {
2253                for se in self.sub_elements() {
2254                    if let Some(mut subelem) = se.0.try_write()
2255                        && subelem.file_membership.is_empty()
2256                    {
2257                        subelem.file_membership.clone_from(&current_fileset);
2258                    }
2259                }
2260            }
2261
2262            let mut extended_fileset = current_fileset;
2263            extended_fileset.insert(weak_file);
2264            // if the parent is splittable, or if the current element already has a fileset, then that fileset should be updated
2265            let parent_splittable = self.parent()?.is_none_or(|p| p.element_type().splittable() != 0);
2266            if parent_splittable || local {
2267                self.0.write().file_membership = extended_fileset;
2268            }
2269
2270            // recursively continue with the parent
2271            if let Some(parent) = self.parent()? {
2272                parent.add_to_file_restricted(file)?;
2273            }
2274        }
2275
2276        Ok(())
2277    }
2278
2279    /// remove this element from a file
2280    ///
2281    /// If the model consists of multiple files, then the set of files in
2282    /// which this element appears will be restricted.
2283    /// It may be required to also omit its parent(s), up to the next splittable point.
2284    ///
2285    /// If the element is only present in single file then an attempt to delete it will be made instead.
2286    /// Deleting the element fails if the element is the root AUTOSAR element, or if it is a SHORT-NAME.
2287    ///
2288    /// # Example
2289    /// ```
2290    /// # use autosar_data::*;
2291    /// # use std::collections::HashSet;
2292    /// # let model = AutosarModel::new();
2293    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
2294    /// # let file2 = model.create_file("test2", AutosarVersion::Autosar_00050).unwrap();
2295    /// # let element = model.root_element();
2296    /// assert!(model.files().count() > 1);
2297    /// element.remove_from_file(&file);
2298    /// ```
2299    ///
2300    /// # Errors
2301    ///
2302    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
2303    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
2304    ///    The operation was aborted to avoid a deadlock, but can be retried.
2305    ///
2306    pub fn remove_from_file(&self, file: &ArxmlFile) -> Result<(), AutosarDataError> {
2307        let parent_splittable = self.parent()?.is_none_or(|p| p.element_type().splittable() != 0);
2308        if parent_splittable {
2309            if file.model()? == self.model()? {
2310                let weak_file = file.downgrade();
2311
2312                // current_fileset is the set of files which contain the current element
2313                let (_, current_fileset) = self.file_membership()?;
2314                let mut restricted_fileset = current_fileset;
2315                restricted_fileset.remove(&weak_file);
2316                if restricted_fileset.is_empty() {
2317                    // the element will no longer be part of any file, so try to delete it
2318                    if let Some(parent) = self.parent()? {
2319                        let _ = parent.remove_sub_element(self.to_owned());
2320                    }
2321                }
2322                // this works even if the element was just removed
2323                self.0.write().file_membership = restricted_fileset;
2324
2325                // update all sub elements with non-default file_membership
2326                let mut to_delete = Vec::new();
2327                for (_, subelem) in self.elements_dfs() {
2328                    // only need to care about those where file_membership is not empty. All other inherit from their parent
2329                    if !subelem.0.read().file_membership.is_empty() {
2330                        subelem.0.write().file_membership.remove(&weak_file);
2331                        // if the file_membership just went to empty, then subelem should be deleted
2332                        if subelem.0.read().file_membership.is_empty() {
2333                            to_delete.push(subelem);
2334                        }
2335                    }
2336                }
2337                // delete elements that are no longer in any file
2338                for delete_elem in to_delete {
2339                    if let Ok(Some(parent)) = delete_elem.parent() {
2340                        let _ = parent.remove_sub_element(delete_elem);
2341                    }
2342                }
2343
2344                Ok(())
2345            } else {
2346                // adding a file from a different model is not permitted
2347                Err(AutosarDataError::InvalidFile)
2348            }
2349        } else {
2350            Err(AutosarDataError::FilesetModificationForbidden)
2351        }
2352    }
2353
2354    /// Return a path that includes non-identifiable elements by their xml names
2355    ///
2356    /// This function cannot fail completely, it will always collect as much information as possible.
2357    /// It is intended for display in error messages.
2358    #[must_use]
2359    pub fn xml_path(&self) -> String {
2360        self.0.read().xml_path()
2361    }
2362
2363    /// Find the upper and lower bound on the insert position for a new sub element
2364    ///
2365    /// If the sub element is allowed for this element given its current content, this function
2366    /// returns the lower and upper bound on the position the new sub element could have.
2367    /// If the sub element is not allowed, then an Err is returned instead.
2368    ///
2369    /// The lower and upper bounds are inclusive: lower <= (element insert pos) <= upper.
2370    /// In many situations lower == upper, this means there is only a single valid position.
2371    ///
2372    /// # Example
2373    /// ```
2374    /// # use autosar_data::*;
2375    /// # fn main() -> Result<(), AutosarDataError> {
2376    /// # use std::collections::HashSet;
2377    /// # let model = AutosarModel::new();
2378    /// # model.create_file("test", AutosarVersion::LATEST).unwrap();
2379    /// let (lbound, ubound) = model.root_element()
2380    ///     .calc_element_insert_range(ElementName::ArPackages, AutosarVersion::LATEST)?;
2381    /// model.root_element().create_sub_element_at(ElementName::ArPackages, lbound)?;
2382    /// # Ok(())
2383    /// # }
2384    /// ```
2385    ///
2386    /// # Errors
2387    ///
2388    /// - [`AutosarDataError::ElementInsertionConflict`]: The sub element conflicts with an existing sub element
2389    /// - [`AutosarDataError::InvalidSubElement`]: The sub element is not valid inside this element
2390    pub fn calc_element_insert_range(
2391        &self,
2392        element_name: ElementName,
2393        version: AutosarVersion,
2394    ) -> Result<(usize, usize), AutosarDataError> {
2395        self.0.read().calc_element_insert_range(element_name, version)
2396    }
2397
2398    /// Return the comment attachd to the element (if any)
2399    ///
2400    /// A comment directly preceding the opening tag is considered to be atached and is returned here.
2401    ///
2402    /// In the arxml text:
2403    /// ```xml
2404    ///     <!--element comment-->
2405    ///     <ELEMENT> ...
2406    /// ```
2407    ///
2408    /// # Example
2409    ///
2410    /// ```
2411    /// # use autosar_data::*;
2412    /// # use std::collections::HashSet;
2413    /// # let model = AutosarModel::new();
2414    /// # let file = model.create_file("test", AutosarVersion::LATEST).unwrap();
2415    /// # let element = model.root_element();
2416    /// let opt_comment = element.comment();
2417    /// ```
2418    #[must_use]
2419    pub fn comment(&self) -> Option<String> {
2420        self.0.read().comment.clone()
2421    }
2422
2423    /// Set or delete the comment attached to the element
2424    ///
2425    /// Set None to remove the comment.
2426    ///
2427    /// If the new comment value contains "--", then this is replaced with "__", because "--" is forbidden inside XML comments.
2428    ///
2429    /// # Example
2430    ///
2431    /// ```
2432    /// # use autosar_data::*;
2433    /// # use std::collections::HashSet;
2434    /// # let model = AutosarModel::new();
2435    /// # let file = model.create_file("test", AutosarVersion::LATEST).unwrap();
2436    /// # let element = model.root_element();
2437    /// # let string = "".to_string();
2438    /// element.set_comment(Some(string));
2439    /// ```
2440    pub fn set_comment(&self, mut opt_comment: Option<String>) {
2441        if let Some(comment) = &mut opt_comment {
2442            // make sure the comment we store never contains "--" as this is forbidden by the w3 xml specification
2443            if comment.contains("--") {
2444                *comment = comment.replace("--", "__");
2445            }
2446        }
2447        self.0.write().comment = opt_comment;
2448    }
2449
2450    /// find the minumum version of all arxml files which contain this element
2451    ///
2452    /// Typically this reduces to finding out which single file contains the element and returning this version.
2453    ///
2454    /// # Example
2455    ///
2456    /// ```
2457    /// # use autosar_data::*;
2458    /// # use std::collections::HashSet;
2459    /// # let model = AutosarModel::new();
2460    /// let file1 = model.create_file("file1", AutosarVersion::LATEST).unwrap();
2461    /// let file2 = model.create_file("file2", AutosarVersion::Autosar_4_3_0).unwrap();
2462    /// let version = model.root_element().min_version().unwrap();
2463    /// assert_eq!(version, AutosarVersion::Autosar_4_3_0);
2464    /// ```
2465    ///
2466    /// # Errors
2467    ///
2468    ///  - [`AutosarDataError::ItemDeleted`]: The current element is in the deleted state and will be freed once the last reference is dropped
2469    ///  - [`AutosarDataError::ParentElementLocked`]: a parent element was locked and did not become available after waiting briefly.
2470    ///    The operation was aborted to avoid a deadlock, but can be retried.
2471    ///  - [`AutosarDataError::NoFilesInModel`]: The operation cannot be completed because the model does not contain any files
2472    pub fn min_version(&self) -> Result<AutosarVersion, AutosarDataError> {
2473        let (_, files) = self.file_membership()?;
2474        let mut ver = AutosarVersion::LATEST;
2475        for f in files.iter().filter_map(WeakArxmlFile::upgrade) {
2476            if f.version() < ver {
2477                ver = f.version();
2478            }
2479        }
2480        Ok(ver)
2481    }
2482}
2483
2484impl Ord for Element {
2485    /// compare the content of two elements
2486    ///
2487    /// This function compares the content of two elements, returning a cmp::Ordering value.
2488    /// The purpose of this function is to allow sorting of elements based on their content.
2489    ///
2490    /// The comparison is performed in the following order:
2491    /// 1. Element name
2492    /// 2. Index (if present)
2493    /// 3. Item name (if present)
2494    /// 4. Definition reference (if present)
2495    /// 5. DEST attribute (if present)
2496    /// 6. Content of the element
2497    /// 7. Attributes of the element
2498    ///
2499    /// If the comparison returns `Ordering::Equal`, then the two elements are identical, but this does not imply that they are the same object.
2500    ///
2501    /// # Example
2502    /// ```
2503    /// # use autosar_data::*;
2504    /// # let model = AutosarModel::new();
2505    /// # let file = model.create_file("test", AutosarVersion::LATEST).unwrap();
2506    /// # let element1 = model.root_element();
2507    /// # let element2 = model.root_element();
2508    /// let ordering = element1.cmp(&element2);
2509    /// ```
2510    fn cmp(&self, other: &Element) -> std::cmp::Ordering {
2511        // Sort by the element name first. This test prevents the other comparisons from being performed when they don't make sense.
2512        match self.element_name().to_str().cmp(other.element_name().to_str()) {
2513            Ordering::Equal => {}
2514            other => return other,
2515        }
2516
2517        // if both elements have an index, then compare the index; if only one has an index, then it comes first
2518        // if neither has an index, then continue and compare other criteria
2519        let index1 = self
2520            .get_sub_element(ElementName::Index)
2521            .and_then(|indexelem| indexelem.character_data())
2522            .and_then(|cdata| cdata.parse_integer::<u64>());
2523        let index2 = other
2524            .get_sub_element(ElementName::Index)
2525            .and_then(|indexelem| indexelem.character_data())
2526            .and_then(|cdata| cdata.parse_integer::<u64>());
2527        match (index1, index2) {
2528            (Some(idx1), Some(idx2)) => {
2529                let result = idx1.cmp(&idx2);
2530                if result != Ordering::Equal {
2531                    return result;
2532                }
2533            }
2534            (Some(_), None) => return std::cmp::Ordering::Less,
2535            (None, Some(_)) => return std::cmp::Ordering::Greater,
2536            (None, None) => {}
2537        }
2538
2539        // sort by item name if present
2540        if let (Some(name1), Some(name2)) = (self.item_name(), other.item_name()) {
2541            // both items have a name - try to decompose the name into a base and an index
2542            // this allows for a more natural sorting of indexed items (e.g. "item2" < "item10")
2543            if let (Some((base1, idx1)), Some((base2, idx2))) =
2544                (decompose_item_name(&name1), decompose_item_name(&name2))
2545                && base1 == base2
2546            {
2547                let result = idx1.cmp(&idx2);
2548                if result != Ordering::Equal {
2549                    return result;
2550                }
2551            }
2552            // if the decomposition fails, then just compare the full item names
2553            let result = name1.cmp(&name2);
2554            if result != Ordering::Equal {
2555                return result;
2556            }
2557        }
2558
2559        // for BSW values: compare the definition references
2560        let definition1 = self
2561            .get_sub_element(ElementName::DefinitionRef)
2562            .and_then(|defref| defref.character_data())
2563            .and_then(|cdata| cdata.string_value());
2564        let definition2 = other
2565            .get_sub_element(ElementName::DefinitionRef)
2566            .and_then(|defref| defref.character_data())
2567            .and_then(|cdata| cdata.string_value());
2568        if let (Some(def1), Some(def2)) = (definition1, definition2) {
2569            let result = def1.cmp(&def2);
2570            if result != Ordering::Equal {
2571                return result;
2572            }
2573        }
2574
2575        // for references: compare the DEST attribute
2576        let dest1 = self
2577            .attribute_value(AttributeName::Dest)
2578            .and_then(|cdata| cdata.enum_value());
2579        let dest2 = other
2580            .attribute_value(AttributeName::Dest)
2581            .and_then(|cdata| cdata.enum_value());
2582        match (dest1, dest2) {
2583            (Some(dest1), Some(dest2)) => {
2584                let result = dest1.to_str().cmp(dest2.to_str());
2585                if result != Ordering::Equal {
2586                    return result;
2587                }
2588            }
2589            (Some(_), None) => return std::cmp::Ordering::Less,
2590            (None, Some(_)) => return std::cmp::Ordering::Greater,
2591            (None, None) => {}
2592        }
2593
2594        // if all else fails, compare the content of the elements
2595        let locked_self = self.0.read();
2596        let locked_other = other.0.read();
2597        locked_self
2598            .content
2599            .cmp(&locked_other.content)
2600            .then(locked_self.attributes.cmp(&locked_other.attributes))
2601    }
2602}
2603
2604impl PartialOrd for Element {
2605    fn partial_cmp(&self, other: &Element) -> Option<std::cmp::Ordering> {
2606        Some(self.cmp(other))
2607    }
2608}
2609
2610/// decompose an item name into a base name and an index
2611/// The index is expected to be a decimal number at the end of the string
2612///
2613/// E.g. "item123" -> ("item", 123)
2614fn decompose_item_name(name: &str) -> Option<(String, u64)> {
2615    let bytestr = name.as_bytes();
2616    let mut pos = bytestr.len();
2617    while pos > 0 && bytestr[pos - 1].is_ascii_digit() {
2618        pos -= 1;
2619    }
2620    if let Ok(index) = name[pos..].parse() {
2621        Some((name[0..pos].to_owned(), index))
2622    } else {
2623        None
2624    }
2625}
2626
2627// a helper that provides compact debug output for the content of an element
2628struct ElementContentFormatter<'a>(&'a SmallVec<[ElementContent; 4]>);
2629impl std::fmt::Debug for ElementContentFormatter<'_> {
2630    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2631        let mut list_fmt = f.debug_list();
2632        for item in self.0.iter() {
2633            match item {
2634                ElementContent::Element(elem) => list_fmt.entry(&elem.element_name()),
2635                ElementContent::CharacterData(cdata) => list_fmt.entry(&cdata),
2636            };
2637        }
2638        list_fmt.finish()
2639    }
2640}
2641
2642// A custom type is needed in order to print a custom value in the Debug implementation without double quoting
2643struct DebugDisplay<'a>(&'a str);
2644impl std::fmt::Debug for DebugDisplay<'_> {
2645    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2646        f.write_str(self.0)
2647    }
2648}
2649
2650// custom debug implementation: print the content instead of only showing the pointer of the Arc
2651impl std::fmt::Debug for Element {
2652    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2653        let elem = self.0.read();
2654        let mut dbgstruct = f.debug_struct("Element");
2655        if let Some(name) = elem.item_name() {
2656            dbgstruct.field("name", &name);
2657        }
2658        dbgstruct.field("elemname", &elem.elemname);
2659        dbgstruct.field("elemtype", &elem.elemtype);
2660        dbgstruct.field("parent", &elem.parent);
2661        dbgstruct.field("content", &ElementContentFormatter(&elem.content));
2662        dbgstruct.field("attributes", &elem.attributes);
2663        // only print the file membership if the element is splittable
2664        // elements that are not splittable may not modify their file membership
2665        if elem.elemtype.splittable() != 0 {
2666            if elem.file_membership.is_empty() {
2667                dbgstruct.field("file_membership", &DebugDisplay("(inherited)"));
2668            } else {
2669                dbgstruct.field("file_membership", &elem.file_membership);
2670            }
2671        }
2672        dbgstruct.finish()
2673    }
2674}
2675
2676impl PartialEq for Element {
2677    fn eq(&self, other: &Self) -> bool {
2678        Arc::as_ptr(&self.0) == Arc::as_ptr(&other.0)
2679    }
2680}
2681
2682impl Eq for Element {}
2683
2684impl Hash for Element {
2685    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2686        state.write_usize(Arc::as_ptr(&self.0) as usize);
2687    }
2688}
2689
2690impl std::fmt::Debug for WeakElement {
2691    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2692        if let Some(elem) = self.upgrade() {
2693            // write!(f, "Element:WeakRef {:p}", Arc::as_ptr(&elem.0));
2694            f.write_fmt(format_args!("Element:WeakRef ({})", elem.element_name()))
2695        } else {
2696            f.write_fmt(format_args!("Element:WeakRef {:p} (invalid)", Weak::as_ptr(&self.0)))
2697        }
2698    }
2699}
2700
2701impl WeakElement {
2702    /// try to get a strong reference to the [Element]
2703    pub fn upgrade(&self) -> Option<Element> {
2704        Weak::upgrade(&self.0).map(Element)
2705    }
2706}
2707
2708impl PartialEq for WeakElement {
2709    fn eq(&self, other: &Self) -> bool {
2710        Weak::as_ptr(&self.0) == Weak::as_ptr(&other.0)
2711    }
2712}
2713
2714impl Eq for WeakElement {}
2715
2716impl Hash for WeakElement {
2717    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2718        state.write_usize(Weak::as_ptr(&self.0) as usize);
2719    }
2720}
2721
2722impl ElementContent {
2723    /// returns the element contained inside this `ElementContent`, or None if the content is `CharacterData`
2724    #[must_use]
2725    pub fn unwrap_element(&self) -> Option<Element> {
2726        if let ElementContent::Element(element) = self {
2727            Some(element.clone())
2728        } else {
2729            None
2730        }
2731    }
2732
2733    /// returns the `CharacterData` inside this `ElementContent`, or None if the content is an Element
2734    #[must_use]
2735    pub fn unwrap_cdata(&self) -> Option<CharacterData> {
2736        if let ElementContent::CharacterData(cdata) = self {
2737            Some(cdata.clone())
2738        } else {
2739            None
2740        }
2741    }
2742}
2743
2744// custom debug implementation: skip printing any content, since the content is only "WeakRef(0x...)"
2745impl std::fmt::Debug for ElementOrModel {
2746    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2747        match self {
2748            ElementOrModel::Element(_) => f.write_str("Element"),
2749            ElementOrModel::Model(_) => f.write_str("Model"),
2750            ElementOrModel::None => f.write_str("None/Invalid"),
2751        }
2752    }
2753}
2754
2755// custom debug implementation: skip printing the name of the wrapper-enum and directly show the content
2756impl std::fmt::Debug for ElementContent {
2757    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2758        match self {
2759            ElementContent::Element(elem) => elem.fmt(f),
2760            ElementContent::CharacterData(cdata) => cdata.fmt(f),
2761        }
2762    }
2763}
2764
2765#[cfg(test)]
2766mod test {
2767    use crate::*;
2768    use std::ffi::OsString;
2769
2770    const BASIC_AUTOSAR_FILE: &str = r#"<?xml version="1.0" encoding="utf-8"?>
2771    <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">
2772        <AR-PACKAGES>
2773            <AR-PACKAGE>
2774                <SHORT-NAME>TestPackage</SHORT-NAME>
2775            </AR-PACKAGE>
2776        </AR-PACKAGES>
2777    </AUTOSAR>"#;
2778
2779    #[test]
2780    fn element_creation() {
2781        let model = AutosarModel::new();
2782        model
2783            .load_buffer(BASIC_AUTOSAR_FILE.as_bytes(), OsString::from("test.arxml"), true)
2784            .unwrap();
2785        let el_autosar = model.root_element();
2786        let el_ar_package = model.get_element_by_path("/TestPackage").unwrap();
2787
2788        let el_elements = el_ar_package.create_sub_element(ElementName::Elements).unwrap();
2789        let el_compu_method = el_elements
2790            .create_named_sub_element(ElementName::CompuMethod, "TestCompuMethod")
2791            .unwrap();
2792        el_elements
2793            .create_named_sub_element(ElementName::CompuMethod, "TestCompuMethod2")
2794            .unwrap();
2795        el_elements
2796            .create_named_sub_element(ElementName::CompuMethod, "TestCompuMethod3")
2797            .unwrap();
2798        // elements with duplicate names are not allowed
2799        assert!(
2800            el_elements
2801                .create_named_sub_element(ElementName::CompuMethod, "TestCompuMethod3")
2802                .is_err()
2803        );
2804
2805        let count = el_elements.sub_elements().count();
2806        assert_eq!(count, 3);
2807        assert_eq!(count, el_elements.content_item_count());
2808
2809        // inserting another COMPU-METHOD into ELEMENTS hould be allowed at any position
2810        let (start_pos, end_pos) = el_elements
2811            .0
2812            .read()
2813            .calc_element_insert_range(ElementName::CompuMethod, AutosarVersion::Autosar_00050)
2814            .unwrap();
2815        assert_eq!(start_pos, 0);
2816        assert_eq!(end_pos, 3); // upper limit is 3 since there are currently 3 elements
2817
2818        // check if create_named_sub_element correctly registered the element in the hashmap so that it can be found
2819        let el_compu_method_test = model.get_element_by_path("/TestPackage/TestCompuMethod").unwrap();
2820        assert_eq!(el_compu_method, el_compu_method_test);
2821
2822        // create more hierarchy
2823        let el_compu_internal_to_phys = el_compu_method
2824            .create_sub_element(ElementName::CompuInternalToPhys)
2825            .unwrap();
2826        let el_compu_scales = el_compu_internal_to_phys
2827            .create_sub_element(ElementName::CompuScales)
2828            .unwrap();
2829        let el_compu_scale = el_compu_scales.create_sub_element(ElementName::CompuScale).unwrap();
2830        el_compu_scale.create_sub_element(ElementName::Desc).unwrap();
2831
2832        // SHORT-LABEL should only be allowed before DESC inside COMPU-SCALE
2833        let (start_pos, end_pos) = el_compu_scale
2834            .calc_element_insert_range(ElementName::ShortLabel, AutosarVersion::Autosar_00050)
2835            .unwrap();
2836        assert_eq!(start_pos, 0);
2837        assert_eq!(end_pos, 0);
2838
2839        // COMPU-CONST should only be allowed after DESC inside COMPU-SCALE
2840        let (start_pos, end_pos) = el_compu_scale
2841            .calc_element_insert_range(ElementName::CompuConst, AutosarVersion::Autosar_00050)
2842            .unwrap();
2843        assert_eq!(start_pos, 1);
2844        assert_eq!(end_pos, 1);
2845
2846        // create COMPU-RATIONAL-COEFFS in COMPU-SCALE. It's presence excludes COMPU-CONST from being inserted
2847        el_compu_scale
2848            .create_sub_element(ElementName::CompuRationalCoeffs)
2849            .unwrap();
2850        // try to insert COMPU-CONST anyway
2851        let result = el_compu_scale.calc_element_insert_range(ElementName::CompuConst, AutosarVersion::Autosar_00050);
2852        assert!(result.is_err());
2853        // it is also not possible to create a second COMPU-RATIONAL-COEFFS
2854        let result =
2855            el_compu_scale.calc_element_insert_range(ElementName::CompuRationalCoeffs, AutosarVersion::Autosar_00050);
2856        assert!(result.is_err());
2857
2858        // creating a sub element at an invalid position fails
2859        assert!(
2860            el_elements
2861                .create_named_sub_element_at(ElementName::System, "System", 99)
2862                .is_err()
2863        );
2864        assert!(el_autosar.create_sub_element_at(ElementName::AdminData, 99).is_err());
2865
2866        // an identifiable element cannot be created without a name
2867        assert!(el_elements.create_sub_element(ElementName::System).is_err());
2868        // the name for an identifiable element must be valid according to the rules
2869        assert!(el_elements.create_named_sub_element(ElementName::System, "").is_err());
2870        assert!(
2871            el_elements
2872                .create_named_sub_element(ElementName::System, "abc def")
2873                .is_err()
2874        );
2875
2876        // a non-identifiable element cannot be created with a name
2877        assert!(
2878            el_autosar
2879                .create_named_sub_element(ElementName::AdminData, "AdminData")
2880                .is_err()
2881        );
2882
2883        // only valid sub-elements can be created
2884        assert!(
2885            el_autosar
2886                .create_named_sub_element(ElementName::Autosar, "Autosar")
2887                .is_err()
2888        );
2889        assert!(
2890            el_autosar
2891                .create_named_sub_element_at(ElementName::Autosar, "Autosar", 0)
2892                .is_err()
2893        );
2894        assert!(el_autosar.create_sub_element(ElementName::Autosar).is_err());
2895        assert!(el_autosar.create_sub_element_at(ElementName::Autosar, 0).is_err());
2896
2897        // creating a sub element fails when any parent element in the hierarchy is locked for writing
2898        let el_autosar_locked = el_autosar.0.write();
2899        assert!(
2900            el_elements
2901                .create_named_sub_element(ElementName::System, "System")
2902                .is_err()
2903        );
2904        assert!(
2905            el_elements
2906                .create_named_sub_element_at(ElementName::System, "System", 0)
2907                .is_err()
2908        );
2909        assert!(el_autosar.create_sub_element(ElementName::AdminData).is_err());
2910        assert!(el_autosar.create_sub_element_at(ElementName::AdminData, 0).is_err());
2911        drop(el_autosar_locked);
2912    }
2913
2914    #[test]
2915    fn parent() {
2916        let model = AutosarModel::new();
2917        model.create_file("test.arxml", AutosarVersion::Autosar_00050).unwrap();
2918        let el_autosar = model.root_element();
2919        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
2920        let el_ar_package = el_ar_packages
2921            .create_named_sub_element(ElementName::ArPackage, "Package")
2922            .unwrap();
2923        let el_elements = el_ar_package.create_sub_element(ElementName::Elements).unwrap();
2924        let el_system = el_elements
2925            .create_named_sub_element(ElementName::System, "Sys")
2926            .unwrap();
2927        let el_fibex = el_system.create_sub_element(ElementName::FibexElements).unwrap();
2928        let el_fibex_cond = el_fibex
2929            .create_sub_element(ElementName::FibexElementRefConditional)
2930            .unwrap();
2931
2932        let parent = el_fibex_cond.parent().unwrap().unwrap();
2933        assert_eq!(parent, el_fibex);
2934        let named_parent = el_fibex_cond.named_parent().unwrap().unwrap();
2935        assert_eq!(named_parent, el_system);
2936
2937        let named_parent = el_system.named_parent().unwrap().unwrap();
2938        assert_eq!(named_parent, el_ar_package);
2939
2940        let named_parent = el_autosar.named_parent().unwrap();
2941        assert!(named_parent.is_none());
2942
2943        // trying to get the named parent of a removed element should fail
2944        el_autosar.remove_sub_element(el_ar_packages).unwrap();
2945        assert!(el_ar_package.named_parent().is_err());
2946    }
2947
2948    #[test]
2949    fn package() {
2950        let model = AutosarModel::new();
2951        model.create_file("test.arxml", AutosarVersion::Autosar_00050).unwrap();
2952        let el_autosar = model.root_element();
2953        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
2954        let el_ar_package = el_ar_packages
2955            .create_named_sub_element(ElementName::ArPackage, "Package")
2956            .unwrap();
2957        let el_ar_packages_2 = el_ar_package.create_sub_element(ElementName::ArPackages).unwrap();
2958        let el_ar_package_2 = el_ar_packages_2
2959            .create_named_sub_element(ElementName::ArPackage, "SubPackage")
2960            .unwrap();
2961
2962        assert_eq!(el_ar_package.package().unwrap(), None); // top level package has no parent package
2963        assert_eq!(el_ar_packages_2.package().unwrap().unwrap(), el_ar_package);
2964        assert_eq!(el_ar_package_2.package().unwrap().unwrap(), el_ar_package); // SubPackage -> Package
2965
2966        drop(el_autosar);
2967        drop(model);
2968        assert!(el_ar_package.package().is_err()); // the model is dropped, so the package cannot be accessed anymore
2969    }
2970
2971    #[test]
2972    fn element_rename() {
2973        let model = AutosarModel::new();
2974        model.create_file("test.arxml", AutosarVersion::Autosar_00050).unwrap();
2975        let el_autosar = model.root_element();
2976        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
2977        let el_ar_package = el_ar_packages
2978            .create_named_sub_element(ElementName::ArPackage, "Package")
2979            .unwrap();
2980        let el_elements = el_ar_package.create_sub_element(ElementName::Elements).unwrap();
2981        let el_can_cluster = el_elements
2982            .create_named_sub_element_at(ElementName::CanCluster, "CanCluster", 0)
2983            .unwrap();
2984        let el_can_physical_channel = el_can_cluster
2985            .create_sub_element(ElementName::CanClusterVariants)
2986            .and_then(|ccv| ccv.create_sub_element(ElementName::CanClusterConditional))
2987            .and_then(|ccc| ccc.create_sub_element(ElementName::PhysicalChannels))
2988            .and_then(|pc| pc.create_named_sub_element(ElementName::CanPhysicalChannel, "CanPhysicalChannel"))
2989            .unwrap();
2990
2991        let el_can_frame_triggering = el_can_physical_channel
2992            .create_sub_element_at(ElementName::FrameTriggerings, 1)
2993            .and_then(|ft| ft.create_named_sub_element(ElementName::CanFrameTriggering, "CanFrameTriggering"))
2994            .unwrap();
2995
2996        let el_ar_package2 = el_ar_packages
2997            .create_named_sub_element(ElementName::ArPackage, "Package2")
2998            .unwrap();
2999        let el_can_frame = el_ar_package2
3000            .create_sub_element(ElementName::Elements)
3001            .and_then(|e| e.create_named_sub_element(ElementName::CanFrame, "CanFrame"))
3002            .unwrap();
3003        let el_frame_ref = el_can_frame_triggering
3004            .create_sub_element(ElementName::FrameRef)
3005            .unwrap();
3006        let _ = el_frame_ref.set_reference_target(&el_can_frame);
3007
3008        // initial value of the reference
3009        let refstr = el_frame_ref.character_data().unwrap().string_value().unwrap();
3010        assert_eq!(refstr, "/Package2/CanFrame");
3011
3012        // empty name, renaming should fail
3013        let result = el_ar_package.set_item_name("");
3014        assert!(result.is_err());
3015
3016        // rename 1. package
3017        el_ar_package.set_item_name("NewPackage").unwrap();
3018        // setting the current name again - should be a no-op
3019        el_ar_package.set_item_name("NewPackage").unwrap();
3020
3021        // duplicate name for Package2, renaming should fail
3022        let result = el_ar_package2.set_item_name("NewPackage");
3023        assert!(result.is_err());
3024
3025        // rename package 2 with a valid name
3026        el_ar_package2.set_item_name("OtherPackage").unwrap();
3027        let refstr = el_frame_ref.character_data().unwrap().string_value().unwrap();
3028        assert_eq!(refstr, "/OtherPackage/CanFrame");
3029
3030        // make sure get_reference_target still works after renaming
3031        let el_can_frame2 = el_frame_ref.get_reference_target().unwrap();
3032        assert_eq!(el_can_frame, el_can_frame2);
3033
3034        // rename the CanFrame as well
3035        el_can_frame.set_item_name("CanFrame_renamed").unwrap();
3036        let refstr = el_frame_ref.character_data().unwrap().string_value().unwrap();
3037        assert_eq!(refstr, "/OtherPackage/CanFrame_renamed");
3038
3039        // invalid element
3040        assert!(el_autosar.set_item_name("Autosar").is_err());
3041
3042        // invalid preconditions
3043        let el_autosar_locked = el_autosar.0.write();
3044        // fails because a parent element is locked
3045        assert!(el_ar_package.set_item_name("TestPackage_renamed").is_err());
3046        drop(el_autosar_locked);
3047        drop(model);
3048        // the reference count of model is now zero, so set_item_name can't get a new reference to it
3049        assert!(el_ar_package.set_item_name("TestPackage_renamed").is_err());
3050    }
3051
3052    #[test]
3053    fn element_copy() {
3054        let model = AutosarModel::new();
3055        model
3056            .load_buffer(BASIC_AUTOSAR_FILE.as_bytes(), OsString::from("test.arxml"), true)
3057            .unwrap();
3058        model.create_file("test", AutosarVersion::LATEST).unwrap();
3059        let el_ar_package = model.get_element_by_path("/TestPackage").unwrap();
3060        el_ar_package
3061            .set_attribute(AttributeName::Uuid, CharacterData::String("0123456".to_string()))
3062            .unwrap();
3063        let el_elements = el_ar_package.create_sub_element(ElementName::Elements).unwrap();
3064        let el_compu_method = el_elements
3065            .create_named_sub_element(ElementName::CompuMethod, "CompuMethod")
3066            .unwrap();
3067        el_elements
3068            .create_named_sub_element(ElementName::DdsServiceInstanceToMachineMapping, "ApItem")
3069            .unwrap();
3070        el_elements
3071            .create_named_sub_element(ElementName::AclObjectSet, "AclObjectSet")
3072            .and_then(|el| el.create_sub_element(ElementName::DerivedFromBlueprintRefs))
3073            .and_then(|el| el.create_sub_element(ElementName::DerivedFromBlueprintRef))
3074            .and_then(|el| {
3075                el.set_attribute(
3076                    AttributeName::Dest,
3077                    CharacterData::Enum(EnumItem::AbstractImplementationDataType),
3078                )
3079            })
3080            .unwrap();
3081        el_elements
3082            .create_named_sub_element(ElementName::System, "System")
3083            .and_then(|el| el.create_sub_element(ElementName::FibexElements))
3084            .and_then(|el| el.create_sub_element(ElementName::FibexElementRefConditional))
3085            .and_then(|el| el.create_sub_element(ElementName::FibexElementRef))
3086            .and_then(|el| el.set_character_data("/invalid"))
3087            .unwrap();
3088
3089        let project2 = AutosarModel::new();
3090        project2
3091            .create_file("test.arxml", AutosarVersion::Autosar_00044)
3092            .unwrap();
3093
3094        // it should not be possible to create an AR-PACKAGE element directly in the AUTOSAR element by copying data
3095        let result = project2.root_element().create_copied_sub_element(&el_ar_package);
3096        assert!(result.is_err());
3097
3098        // create an AR-PACKAGES element and copy the data there. This should succeed.
3099        // the copied data shoud contain the COMPU-METHOD, but not the DDS-SERVICE-INSTANCE-TO-MACHINE-MAPPING
3100        // because the latter was specified in Adaptive 18-03 (Autosar_00045) and is not valid in Autosar_00044
3101        let el_ar_packages2 = project2
3102            .root_element()
3103            .create_sub_element(ElementName::ArPackages)
3104            .unwrap();
3105        el_ar_packages2.create_copied_sub_element(&el_ar_package).unwrap();
3106
3107        // it should be possible to look up the copied compu-method by its path
3108        let el_compu_method_2 = project2.get_element_by_path("/TestPackage/CompuMethod").unwrap();
3109
3110        // the copy should not refer to the same memory as the original
3111        assert_ne!(el_compu_method, el_compu_method_2);
3112        // the copy should serialize to exactly the same string as the original
3113        assert_eq!(el_compu_method.serialize(), el_compu_method_2.serialize());
3114
3115        // verify that the DDS-SERVICE-INSTANCE-TO-MACHINE-MAPPING element was not copied
3116        let result = project2.get_element_by_path("/TestPackage/ApItem");
3117        assert!(result.is_none());
3118
3119        // make sure the element ordering constraints are considered when copying with the _at() variant
3120        let el_ar_package2 = el_ar_packages2
3121            .create_named_sub_element(ElementName::ArPackage, "Package2")
3122            .unwrap();
3123        let result = el_ar_package2.create_copied_sub_element_at(&el_elements, 0);
3124        assert!(result.is_err()); // position 0 is already used by the SHORT-NAME
3125        let el_elements2 = el_ar_package2.create_sub_element(ElementName::Elements).unwrap();
3126        let result = el_elements2.create_copied_sub_element_at(&el_compu_method, 99);
3127        assert!(result.is_err()); // position 99 is not valid
3128        let result = el_elements2.create_copied_sub_element_at(&el_compu_method, 0);
3129        assert!(result.is_ok()); // position 0 is valid
3130
3131        // can't copy an element that is not a valid sub element here
3132        let result = el_ar_package2.create_copied_sub_element_at(&el_compu_method, 0);
3133        assert!(result.is_err()); // COMPU-METHOS id not a valid sub-element of AR-PACKAGE
3134    }
3135
3136    #[test]
3137    fn element_copy_loop() {
3138        let model = AutosarModel::new();
3139        model.create_file("test.arxml", AutosarVersion::Autosar_00050).unwrap();
3140        let el_autosar = model.root_element();
3141        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
3142        let el_ar_package = el_ar_packages
3143            .create_named_sub_element(ElementName::ArPackage, "Pkg")
3144            .unwrap();
3145
3146        let result = el_ar_package.create_copied_sub_element(&el_ar_packages);
3147        assert!(result.is_err());
3148
3149        // copying an element into itself should return an error and should not deadlock
3150        let result = el_ar_package.create_copied_sub_element(&el_ar_package);
3151        assert!(result.is_err());
3152        let result = el_ar_package.create_copied_sub_element_at(&el_ar_package, 0);
3153        assert!(result.is_err());
3154    }
3155
3156    #[test]
3157    fn element_deletion() {
3158        let model = AutosarModel::new();
3159        model
3160            .load_buffer(BASIC_AUTOSAR_FILE.as_bytes(), OsString::from("test.arxml"), true)
3161            .unwrap();
3162        let el_ar_package = model.get_element_by_path("/TestPackage").unwrap();
3163        let el_short_name = el_ar_package.get_sub_element(ElementName::ShortName).unwrap();
3164        el_ar_package
3165            .create_sub_element(ElementName::Elements)
3166            .and_then(|el| el.create_named_sub_element(ElementName::System, "System"))
3167            .and_then(|el| el.create_sub_element(ElementName::FibexElements))
3168            .and_then(|el| el.create_sub_element(ElementName::FibexElementRefConditional))
3169            .and_then(|el| el.create_sub_element(ElementName::FibexElementRef))
3170            .and_then(|el| el.set_character_data("/invalid"))
3171            .unwrap();
3172
3173        // removing the SHORT-NAME of an identifiable element is forbidden
3174        let result = el_ar_package.remove_sub_element(el_short_name);
3175        if let Err(AutosarDataError::ShortNameRemovalForbidden) = result {
3176            // correct
3177        } else {
3178            panic!("Removing the SHORT-NAME was not prohibited");
3179        }
3180        let el_ar_package_clone = el_ar_package.clone();
3181        let el_ar_packages = el_ar_package.parent().unwrap().unwrap();
3182        let result = el_ar_packages.remove_sub_element(el_ar_package);
3183        // deleting identifiable elements should also cause the cached references to them to be removed
3184        assert_eq!(model.0.read().identifiables.len(), 0);
3185        assert!(result.is_ok());
3186
3187        // alternative: remove_sub_element_kind
3188        el_ar_packages
3189            .create_named_sub_element(ElementName::ArPackage, "SecondPackage")
3190            .unwrap();
3191        assert_eq!(el_ar_packages.content_item_count(), 1);
3192        let result = el_ar_packages.remove_sub_element_kind(ElementName::ArPackage);
3193        assert!(result.is_ok());
3194        assert_eq!(el_ar_packages.content_item_count(), 0);
3195        let result = el_ar_packages.remove_sub_element_kind(ElementName::ArPackage);
3196        assert!(result.is_err());
3197
3198        // the removed element may still exist if there were other references to it, but it is no longer usable
3199        let result = el_ar_package_clone.parent();
3200        assert!(matches!(result, Err(AutosarDataError::ItemDeleted)));
3201        let result = el_ar_package_clone.model();
3202        assert!(matches!(result, Err(AutosarDataError::ItemDeleted)));
3203        assert_eq!(el_ar_package_clone.position(), None);
3204    }
3205
3206    #[test]
3207    fn element_position() {
3208        let model = AutosarModel::new();
3209        model.create_file("test.arxml", AutosarVersion::Autosar_00050).unwrap();
3210        let el_autosar = model.root_element();
3211        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
3212        let el_ar_package1 = el_ar_packages
3213            .create_named_sub_element(ElementName::ArPackage, "Pkg1")
3214            .unwrap();
3215        let el_ar_package2 = el_ar_packages
3216            .create_named_sub_element(ElementName::ArPackage, "Pkg2")
3217            .unwrap();
3218        let el_ar_package3 = el_ar_packages
3219            .create_named_sub_element(ElementName::ArPackage, "Pkg3")
3220            .unwrap();
3221
3222        assert_eq!(el_ar_packages.content_item_count(), 3);
3223        assert_eq!(el_ar_package2.position().unwrap(), 1);
3224        assert_eq!(el_ar_packages.get_sub_element_at(1).unwrap(), el_ar_package2);
3225        assert_eq!(el_ar_package3.position().unwrap(), 2);
3226        assert_eq!(el_ar_packages.get_sub_element_at(2).unwrap(), el_ar_package3);
3227
3228        // there is no subelement at position 1
3229        let nonexistent = el_ar_package1.get_sub_element_at(1);
3230        assert_eq!(nonexistent, None);
3231    }
3232
3233    #[test]
3234    fn element_type() {
3235        let model = AutosarModel::new();
3236        model.create_file("test.arxml", AutosarVersion::Autosar_00050).unwrap();
3237        let el_autosar = model.root_element();
3238
3239        assert_eq!(el_autosar.element_type(), ElementType::ROOT);
3240    }
3241
3242    #[test]
3243    fn content_type() {
3244        let model = AutosarModel::new();
3245        model.create_file("test.arxml", AutosarVersion::Autosar_00050).unwrap();
3246        let el_autosar = model.root_element();
3247        let el_ar_package = el_autosar
3248            .create_sub_element(ElementName::ArPackages)
3249            .and_then(|ar_pkgs| ar_pkgs.create_named_sub_element(ElementName::ArPackage, "Package"))
3250            .unwrap();
3251        let el_short_name = el_ar_package.get_sub_element(ElementName::ShortName).unwrap();
3252
3253        let el_l4 = el_ar_package
3254            .create_sub_element(ElementName::LongName)
3255            .and_then(|ln| ln.create_sub_element(ElementName::L4))
3256            .unwrap();
3257
3258        let el_elements = el_ar_package.create_sub_element(ElementName::Elements).unwrap();
3259        let el_debounce_algo = el_elements
3260            .create_named_sub_element(ElementName::DiagnosticContributionSet, "DCS")
3261            .and_then(|dcs| dcs.create_sub_element(ElementName::CommonProperties))
3262            .and_then(|cp| cp.create_sub_element(ElementName::DiagnosticCommonPropsVariants))
3263            .and_then(|dcpv| dcpv.create_sub_element(ElementName::DiagnosticCommonPropsConditional))
3264            .and_then(|dcpc| dcpc.create_sub_element(ElementName::DebounceAlgorithmPropss))
3265            .and_then(|dap| dap.create_named_sub_element(ElementName::DiagnosticDebounceAlgorithmProps, "ddap"))
3266            .and_then(|ddap| ddap.create_sub_element(ElementName::DebounceAlgorithm))
3267            .unwrap();
3268
3269        assert_eq!(el_autosar.element_type().content_mode(), ContentMode::Sequence);
3270        assert_eq!(el_autosar.content_type(), ContentType::Elements);
3271        assert_eq!(el_elements.element_type().content_mode(), ContentMode::Bag);
3272        assert_eq!(el_elements.content_type(), ContentType::Elements);
3273        assert_eq!(el_debounce_algo.element_type().content_mode(), ContentMode::Choice);
3274        assert_eq!(el_debounce_algo.content_type(), ContentType::Elements);
3275        assert_eq!(el_short_name.element_type().content_mode(), ContentMode::Characters);
3276        assert_eq!(el_short_name.content_type(), ContentType::CharacterData);
3277        assert_eq!(el_l4.element_type().content_mode(), ContentMode::Mixed);
3278        assert_eq!(el_l4.content_type(), ContentType::Mixed);
3279    }
3280
3281    #[test]
3282    fn attributes() {
3283        let model = AutosarModel::new();
3284        model
3285            .load_buffer(BASIC_AUTOSAR_FILE.as_bytes(), OsString::from("test.arxml"), true)
3286            .unwrap();
3287        model.create_file("test", AutosarVersion::LATEST).unwrap();
3288        let el_autosar = model.root_element();
3289        let el_ar_packages = el_autosar.get_sub_element(ElementName::ArPackages).unwrap();
3290
3291        let count = el_autosar.attributes().count();
3292        assert_eq!(count, 3);
3293
3294        // set the attribute S on the element AUTOSAR
3295        el_autosar
3296            .set_attribute(AttributeName::S, CharacterData::String(String::from("something")))
3297            .unwrap();
3298
3299        // AUTOSAR has no DEST attribute, so this should fail
3300        assert!(
3301            el_autosar
3302                .set_attribute(AttributeName::Dest, CharacterData::String(String::from("something")))
3303                .is_err()
3304        );
3305
3306        // The attribute S exists and is optional, so it can be removed
3307        let result = el_autosar.remove_attribute(AttributeName::S);
3308        assert!(result);
3309
3310        // the attribute xmlns is required and cannot be removed
3311        let result = el_autosar.remove_attribute(AttributeName::xmlns);
3312        assert!(!result);
3313
3314        // the attribute ACCESSKEY does not exist in the element AUTOSAR and cannot be removed
3315        let result = el_autosar.remove_attribute(AttributeName::Accesskey);
3316        assert!(!result);
3317
3318        // the attribute T is permitted on AUTOSAR and the string is a valid value
3319        el_autosar
3320            .set_attribute_string(AttributeName::T, "2022-01-31T13:00:59Z")
3321            .unwrap();
3322
3323        // update an existing attribute
3324        el_autosar
3325            .set_attribute_string(AttributeName::T, "2022-01-31T14:00:59Z")
3326            .unwrap();
3327
3328        // fail set an attribute due to data validation
3329        assert!(el_autosar.set_attribute_string(AttributeName::T, "abc").is_err());
3330
3331        // can't set unknown attributes with set_attribute_string
3332        assert!(
3333            el_ar_packages
3334                .set_attribute_string(AttributeName::xmlns, "abc")
3335                .is_err()
3336        );
3337
3338        // directly return an attribute as a string
3339        let xmlns = el_autosar
3340            .attribute_value(AttributeName::xmlns)
3341            .map(|cdata| cdata.to_string())
3342            .unwrap();
3343        assert_eq!(xmlns, "http://autosar.org/schema/r4.0".to_string());
3344
3345        // attribute operation fails when a parent element is locked for writing
3346        let lock = el_autosar.0.write();
3347        assert!(
3348            el_ar_packages
3349                .set_attribute(AttributeName::Uuid, CharacterData::String(String::from("1234")))
3350                .is_err()
3351        );
3352        assert!(
3353            el_ar_packages
3354                .set_attribute_string(AttributeName::Uuid, "1234")
3355                .is_err()
3356        );
3357        drop(lock);
3358    }
3359
3360    #[test]
3361    fn mixed_content() {
3362        let model = AutosarModel::new();
3363        model
3364            .load_buffer(BASIC_AUTOSAR_FILE.as_bytes(), OsString::from("test.arxml"), true)
3365            .unwrap();
3366        let el_ar_package = model.get_element_by_path("/TestPackage").unwrap();
3367        let el_long_name = el_ar_package.create_sub_element(ElementName::LongName).unwrap();
3368        assert_eq!(el_long_name.content_type(), ContentType::Elements);
3369        let el_l_4 = el_long_name.create_sub_element(ElementName::L4).unwrap();
3370        assert_eq!(el_l_4.content_type(), ContentType::Mixed);
3371
3372        el_l_4.create_sub_element(ElementName::E).unwrap();
3373        el_l_4.insert_character_content_item("foo", 1).unwrap();
3374        el_l_4.create_sub_element(ElementName::Sup).unwrap();
3375        el_l_4.insert_character_content_item("bar", 0).unwrap();
3376        assert_eq!(el_l_4.content().count(), 4);
3377
3378        // character data item "foo" is now in position 2 and gets removed
3379        assert!(el_l_4.remove_character_content_item(2).is_ok());
3380        assert_eq!(el_l_4.content().count(), 3);
3381        // character data item "bar" should be in postion 0
3382        let item = el_l_4.content().next().unwrap();
3383        if let ElementContent::CharacterData(CharacterData::String(content)) = item {
3384            assert_eq!(content, "bar");
3385        } else {
3386            panic!("unexpected content in <L-4>: {item:?}");
3387        }
3388    }
3389
3390    #[test]
3391    fn move_element_position() {
3392        // move an element to a different position within its parent
3393        let model = AutosarModel::new();
3394        model.create_file("test.arxml", AutosarVersion::Autosar_00050).unwrap();
3395        let el_autosar = model.root_element();
3396        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
3397        let pkg1 = el_ar_packages
3398            .create_named_sub_element(ElementName::ArPackage, "Pkg1")
3399            .unwrap();
3400        let pkg2 = el_ar_packages
3401            .create_named_sub_element(ElementName::ArPackage, "Pkg2")
3402            .unwrap();
3403        let pkg3 = el_ar_packages
3404            .create_named_sub_element(ElementName::ArPackage, "Pkg3")
3405            .unwrap();
3406
3407        // "moving" an element inside its parent without actually giving a position is a no-op
3408        el_ar_packages.move_element_here(&pkg3).unwrap();
3409
3410        // moving to an invalid position fails
3411        assert!(el_ar_packages.move_element_here_at(&pkg1, 99).is_err());
3412        assert!(el_ar_packages.move_element_here_at(&pkg1, 3).is_err()); // special boundary case
3413
3414        // move an element forward
3415        el_ar_packages.move_element_here_at(&pkg2, 0).unwrap();
3416        // move an element backward
3417        el_ar_packages.move_element_here_at(&pkg1, 2).unwrap();
3418        // check the new ordering
3419        let mut packages_iter = el_ar_packages.sub_elements();
3420        assert_eq!(packages_iter.next().unwrap(), pkg2);
3421        assert_eq!(packages_iter.next().unwrap(), pkg3);
3422        assert_eq!(packages_iter.next().unwrap(), pkg1);
3423
3424        // moving elements should also work with mixed content
3425        let el_l_4 = pkg1
3426            .create_sub_element(ElementName::LongName)
3427            .and_then(|el| el.create_sub_element(ElementName::L4))
3428            .unwrap();
3429        el_l_4.create_sub_element(ElementName::E).unwrap();
3430        el_l_4.insert_character_content_item("foo", 1).unwrap();
3431        let el_sup = el_l_4.create_sub_element(ElementName::Sup).unwrap();
3432        el_l_4.insert_character_content_item("bar", 0).unwrap();
3433        el_l_4.move_element_here_at(&el_sup, 0).unwrap();
3434        let mut iter = el_l_4.sub_elements();
3435        assert_eq!(iter.next().unwrap(), el_sup);
3436    }
3437
3438    #[test]
3439    fn move_element_local() {
3440        // move an element within the same model
3441        let model = AutosarModel::new();
3442        model.create_file("test.arxml", AutosarVersion::Autosar_00050).unwrap();
3443        let el_autosar = model.root_element();
3444        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
3445        let el_pkg1 = el_ar_packages
3446            .create_named_sub_element(ElementName::ArPackage, "Pkg1")
3447            .unwrap();
3448        let el_elements1 = el_pkg1.create_sub_element(ElementName::Elements).unwrap();
3449        let el_ecu_instance = el_elements1
3450            .create_named_sub_element(ElementName::EcuInstance, "EcuInstance")
3451            .unwrap();
3452        let el_pkg2 = el_ar_packages
3453            .create_named_sub_element(ElementName::ArPackage, "Pkg2")
3454            .unwrap();
3455        let el_pkg3 = el_ar_packages
3456            .create_named_sub_element(ElementName::ArPackage, "Pkg3")
3457            .unwrap();
3458        let el_fibex_element_ref = el_pkg3
3459            .create_sub_element(ElementName::Elements)
3460            .and_then(|el| el.create_named_sub_element(ElementName::System, "System"))
3461            .and_then(|el| el.create_sub_element(ElementName::FibexElements))
3462            .and_then(|el| el.create_sub_element(ElementName::FibexElementRefConditional))
3463            .and_then(|el| el.create_sub_element(ElementName::FibexElementRef))
3464            .unwrap();
3465        el_fibex_element_ref.set_reference_target(&el_ecu_instance).unwrap();
3466
3467        // can't move an element of the wrong type
3468        assert!(el_ar_packages.move_element_here(&el_autosar).is_err());
3469        assert!(el_ar_packages.move_element_here_at(&el_autosar, 0).is_err());
3470
3471        // moving an element into its own sub element (creating a loop) is forbidden
3472        assert!(el_pkg1.move_element_here(&el_ar_packages).is_err());
3473        assert!(el_pkg1.move_element_here_at(&el_ar_packages, 1).is_err());
3474
3475        // move an unnamed element
3476        assert!(model.get_element_by_path("/Pkg1/EcuInstance").is_some());
3477        el_pkg2.move_element_here(&el_elements1).unwrap();
3478        assert_eq!(el_elements1.parent().unwrap().unwrap(), el_pkg2);
3479        assert!(model.get_element_by_path("/Pkg2/EcuInstance").is_some());
3480        assert_eq!(el_fibex_element_ref.get_reference_target().unwrap(), el_ecu_instance);
3481
3482        // move the unnamed element back using the _at variant
3483        el_pkg1.move_element_here_at(&el_elements1, 1).unwrap();
3484        assert_eq!(el_elements1.parent().unwrap().unwrap(), el_pkg1);
3485        assert!(model.get_element_by_path("/Pkg1/EcuInstance").is_some());
3486        assert_eq!(el_fibex_element_ref.get_reference_target().unwrap(), el_ecu_instance);
3487
3488        // move a named element
3489        let el_elements2 = el_pkg2.create_sub_element(ElementName::Elements).unwrap();
3490        el_elements2.move_element_here(&el_ecu_instance).unwrap();
3491        assert_eq!(el_ecu_instance.parent().unwrap().unwrap(), el_elements2);
3492        assert!(model.get_element_by_path("/Pkg2/EcuInstance").is_some());
3493        assert_eq!(el_fibex_element_ref.get_reference_target().unwrap(), el_ecu_instance);
3494
3495        // moving an element should automatically resolve name conflicts
3496        el_elements1
3497            .create_named_sub_element(ElementName::EcuInstance, "EcuInstance")
3498            .unwrap();
3499        el_elements1.move_element_here_at(&el_ecu_instance, 0).unwrap();
3500        assert_eq!(el_ecu_instance.parent().unwrap().unwrap(), el_elements1);
3501        assert!(model.get_element_by_path("/Pkg1/EcuInstance_1").is_some());
3502        assert_eq!(el_fibex_element_ref.get_reference_target().unwrap(), el_ecu_instance);
3503    }
3504
3505    #[test]
3506    fn move_element_full() {
3507        // move an element between two projects
3508        let model1 = AutosarModel::new();
3509        model1
3510            .create_file("test1.arxml", AutosarVersion::Autosar_00050)
3511            .unwrap();
3512        let el_autosar = model1.root_element();
3513        let el_ar_packages1 = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
3514        let el_pkg1 = el_ar_packages1
3515            .create_named_sub_element(ElementName::ArPackage, "Pkg1")
3516            .unwrap();
3517        let el_elements1 = el_pkg1.create_sub_element(ElementName::Elements).unwrap();
3518        let el_ecu_instance = el_elements1
3519            .create_named_sub_element(ElementName::EcuInstance, "EcuInstance")
3520            .unwrap();
3521        let el_pkg2 = el_ar_packages1
3522            .create_named_sub_element(ElementName::ArPackage, "Pkg2")
3523            .unwrap();
3524        let el_fibex_element_ref = el_pkg2
3525            .create_sub_element(ElementName::Elements)
3526            .and_then(|el| el.create_named_sub_element(ElementName::System, "System"))
3527            .and_then(|el| el.create_sub_element(ElementName::FibexElements))
3528            .and_then(|el| el.create_sub_element(ElementName::FibexElementRefConditional))
3529            .and_then(|el| el.create_sub_element(ElementName::FibexElementRef))
3530            .unwrap();
3531        el_fibex_element_ref.set_reference_target(&el_ecu_instance).unwrap();
3532
3533        let model2 = AutosarModel::new();
3534        model2
3535            .create_file("test2.arxml", AutosarVersion::Autosar_00050)
3536            .unwrap();
3537        let el_autosar2 = model2.root_element();
3538        let el_ar_packages2 = el_autosar2.create_sub_element(ElementName::ArPackages).unwrap();
3539
3540        // move a named element
3541        el_ar_packages2.move_element_here(&el_pkg1).unwrap();
3542        assert!(model1.get_element_by_path("/Pkg1").is_none());
3543        assert!(model2.get_element_by_path("/Pkg1").is_some());
3544        el_ar_packages2.move_element_here_at(&el_pkg2, 1).unwrap();
3545        assert!(model1.get_element_by_path("/Pkg2").is_none());
3546        assert!(model2.get_element_by_path("/Pkg2").is_some());
3547
3548        // move an unnamed element
3549        el_autosar.remove_sub_element(el_ar_packages1).unwrap();
3550        el_autosar.move_element_here(&el_ar_packages2).unwrap();
3551        assert!(model1.get_element_by_path("/Pkg1/EcuInstance").is_some());
3552        assert!(model1.get_element_by_path("/Pkg2/System").is_some());
3553        assert_eq!(el_fibex_element_ref.get_reference_target().unwrap(), el_ecu_instance);
3554
3555        // can't move an element when one of the projects is deleted
3556        drop(model2);
3557        assert!(el_autosar2.move_element_here(&el_ar_packages2).is_err());
3558        assert!(el_autosar2.move_element_here_at(&el_ar_packages2, 0).is_err());
3559
3560        // can't move between files with different versions
3561        let project3 = AutosarModel::new();
3562        project3
3563            .create_file("test2.arxml", AutosarVersion::Autosar_4_3_0)
3564            .unwrap();
3565        let el_autosar3 = project3.root_element();
3566        assert!(el_autosar3.move_element_here(&el_ar_packages2).is_err());
3567        assert!(el_autosar3.move_element_here_at(&el_ar_packages2, 0).is_err());
3568    }
3569
3570    #[test]
3571    fn get_set_reference_target() {
3572        let model = AutosarModel::new();
3573        model.create_file("text.arxml", AutosarVersion::Autosar_00050).unwrap();
3574        let el_autosar = model.root_element();
3575        let el_ar_package = el_autosar
3576            .create_sub_element(ElementName::ArPackages)
3577            .and_then(|arpkgs| arpkgs.create_named_sub_element(ElementName::ArPackage, "Package"))
3578            .unwrap();
3579        let el_elements = el_ar_package.create_sub_element(ElementName::Elements).unwrap();
3580        let el_ecu_instance1 = el_elements
3581            .create_named_sub_element(ElementName::EcuInstance, "EcuInstance1")
3582            .unwrap();
3583        let el_ecu_instance2 = el_elements
3584            .create_named_sub_element(ElementName::EcuInstance, "EcuInstance2")
3585            .unwrap();
3586        let el_req_result = el_elements
3587            .create_named_sub_element(ElementName::DiagnosticRoutine, "DiagRoutine")
3588            .and_then(|dr| dr.create_named_sub_element(ElementName::RequestResult, "RequestResult"))
3589            .unwrap();
3590        let el_fibex_element_ref = el_elements
3591            .create_named_sub_element(ElementName::System, "System")
3592            .and_then(|sys| sys.create_sub_element(ElementName::FibexElements))
3593            .and_then(|fe| fe.create_sub_element(ElementName::FibexElementRefConditional))
3594            .and_then(|ferc| ferc.create_sub_element(ElementName::FibexElementRef))
3595            .unwrap();
3596        let el_physical_request_ref = el_elements
3597            .create_named_sub_element(ElementName::DiagnosticConnection, "DiagnosticConnection")
3598            .and_then(|dc| dc.create_sub_element(ElementName::PhysicalRequestRef))
3599            .unwrap();
3600        let el_connection_ident = el_elements
3601            .create_named_sub_element(ElementName::CanTpConfig, "CanTpConfig")
3602            .and_then(|ctc| ctc.create_sub_element(ElementName::TpConnections))
3603            .and_then(|tc: Element| tc.create_sub_element(ElementName::CanTpConnection))
3604            .and_then(|ctc: Element| ctc.create_named_sub_element(ElementName::Ident, "ConnectionIdent"))
3605            .unwrap();
3606
3607        // set_reference_target does not work for elements which are not references
3608        assert!(el_elements.set_reference_target(&el_ar_package).is_err());
3609        // element AUTOSAR is not identifiable, and a reference to it cannot be set
3610        assert!(el_fibex_element_ref.set_reference_target(&el_autosar).is_err());
3611        // element AR-PACKAGE is identifiable, but not a valid reference target for a FIBEX-ELEMENT-REF
3612        assert!(el_fibex_element_ref.set_reference_target(&el_ar_package).is_err());
3613        // element REQUEST-RESULT is identifiable, but cannot be referenced by any other element as here is no valid DEST enum entry for it
3614        assert!(el_fibex_element_ref.set_reference_target(&el_req_result).is_err());
3615
3616        // set a valid reference and verify that the reference can be used
3617        el_fibex_element_ref.set_reference_target(&el_ecu_instance1).unwrap();
3618        assert_eq!(el_fibex_element_ref.get_reference_target().unwrap(), el_ecu_instance1);
3619        // update with a different valid reference and verify that the reference can be used
3620        el_fibex_element_ref.set_reference_target(&el_ecu_instance2).unwrap();
3621        assert_eq!(el_fibex_element_ref.get_reference_target().unwrap(), el_ecu_instance2);
3622
3623        // define a REFERENCE-BASE and then set a relative reference via BASE
3624        let el_reference_bases = el_ar_package.create_sub_element(ElementName::ReferenceBases).unwrap();
3625        let el_reference_base = el_reference_bases
3626            .create_sub_element(ElementName::ReferenceBase)
3627            .unwrap();
3628        el_reference_base
3629            .create_sub_element(ElementName::ShortLabel)
3630            .and_then(|short_label| short_label.set_character_data("default"))
3631            .unwrap();
3632        el_reference_base
3633            .create_sub_element(ElementName::PackageRef)
3634            .and_then(|package_ref| package_ref.set_reference_target(&el_ar_package))
3635            .unwrap();
3636        assert!(model.0.read().reference_bases.contains_key("default"));
3637
3638        el_fibex_element_ref
3639            .set_relative_reference_target(&el_ecu_instance2, "default")
3640            .unwrap();
3641        assert_eq!(
3642            el_fibex_element_ref
3643                .attribute_value(AttributeName::Base)
3644                .and_then(|cdata| cdata.string_value())
3645                .unwrap(),
3646            "default"
3647        );
3648        assert_eq!(
3649            el_fibex_element_ref
3650                .character_data()
3651                .and_then(|cdata| cdata.string_value())
3652                .unwrap(),
3653            "EcuInstance2"
3654        );
3655        assert_eq!(el_fibex_element_ref.get_reference_target().unwrap(), el_ecu_instance2);
3656        assert!(
3657            model
3658                .get_references_to("/Package/EcuInstance2")
3659                .contains(&el_fibex_element_ref.downgrade())
3660        );
3661
3662        // set a valid reference to <CAN-TP-CONNECTION><IDENT>.
3663        // This is a complex case, as the correct DEST attribute must be looked up in the specification
3664        el_physical_request_ref
3665            .set_reference_target(&el_connection_ident)
3666            .unwrap();
3667        assert_eq!(
3668            el_physical_request_ref.get_reference_target().unwrap(),
3669            el_connection_ident
3670        );
3671
3672        // invalid reference: bad DEST attribute
3673        el_fibex_element_ref
3674            .set_attribute(AttributeName::Dest, CharacterData::Enum(EnumItem::ISignal))
3675            .unwrap();
3676        assert!(el_fibex_element_ref.get_reference_target().is_err());
3677        // invalid reference: no DEST attribute
3678        el_fibex_element_ref.0.write().attributes.clear(); // remove the DEST attribute
3679        assert!(el_fibex_element_ref.get_reference_target().is_err());
3680        el_fibex_element_ref.set_reference_target(&el_ecu_instance2).unwrap();
3681        // invalid reference: bad reference string
3682        el_fibex_element_ref
3683            .set_attribute(AttributeName::Dest, CharacterData::Enum(EnumItem::EcuInstance))
3684            .unwrap();
3685        el_fibex_element_ref.set_character_data("/does/not/exist").unwrap();
3686        assert!(el_fibex_element_ref.get_reference_target().is_err());
3687        // invalid reference: refers to the wrong type of element
3688        el_fibex_element_ref.set_character_data("/Package").unwrap();
3689        assert!(el_fibex_element_ref.get_reference_target().is_err());
3690        // invalid reference: no reference string
3691        el_fibex_element_ref.remove_character_data().unwrap();
3692        assert!(el_fibex_element_ref.get_reference_target().is_err());
3693        el_fibex_element_ref.set_reference_target(&el_ecu_instance2).unwrap();
3694        // not a reference
3695        assert!(el_elements.get_reference_target().is_err());
3696        // model is deleted
3697        drop(model);
3698        assert!(el_fibex_element_ref.get_reference_target().is_err());
3699    }
3700
3701    #[test]
3702    fn modify_character_data() {
3703        let model = AutosarModel::new();
3704        model.create_file("text.arxml", AutosarVersion::Autosar_00050).unwrap();
3705        let el_autosar = model.root_element();
3706        let el_ar_package = el_autosar
3707            .create_sub_element(ElementName::ArPackages)
3708            .and_then(|arpkgs| arpkgs.create_named_sub_element(ElementName::ArPackage, "Package"))
3709            .unwrap();
3710        let el_short_name = el_ar_package.get_sub_element(ElementName::ShortName).unwrap();
3711        let el_elements = el_ar_package.create_sub_element(ElementName::Elements).unwrap();
3712        let el_system = el_elements
3713            .create_named_sub_element(ElementName::System, "System")
3714            .unwrap();
3715        let el_fibex_element_ref = el_system
3716            .create_sub_element(ElementName::FibexElements)
3717            .and_then(|fe| fe.create_sub_element(ElementName::FibexElementRefConditional))
3718            .and_then(|ferc| ferc.create_sub_element(ElementName::FibexElementRef))
3719            .unwrap();
3720        let el_pnc_vector_length = el_system.create_sub_element(ElementName::PncVectorLength).unwrap();
3721
3722        // set character data on an "ordinary" element that has no special handling
3723        assert!(
3724            el_pnc_vector_length
3725                .set_character_data(CharacterData::String("2".to_string()))
3726                .is_ok()
3727        ); // "native" type is String, without automatic wrapping
3728        assert!(el_pnc_vector_length.set_character_data("2".to_string()).is_ok()); // "native" type is String
3729        assert!(el_pnc_vector_length.set_character_data("2").is_ok()); // automatic conversion: &str -> String
3730        assert!(el_pnc_vector_length.set_character_data(2).is_ok()); // automatic conversion: u64 -> String
3731
3732        // set a new SHORT-NAME, this also updates path cache
3733        assert!(
3734            el_short_name
3735                .set_character_data(CharacterData::String("PackageRenamed".to_string()))
3736                .is_ok()
3737        );
3738        assert_eq!(
3739            el_short_name.character_data().unwrap().string_value().unwrap(),
3740            "PackageRenamed"
3741        );
3742        model.get_element_by_path("/PackageRenamed").unwrap();
3743
3744        // set a new reference target, which creates an entry in the reference origin cache
3745        assert!(
3746            el_fibex_element_ref
3747                .set_character_data("/PackageRenamed/EcuInstance1")
3748                .is_ok()
3749        );
3750        model
3751            .0
3752            .read()
3753            .reference_origins
3754            .get("/PackageRenamed/EcuInstance1")
3755            .unwrap();
3756
3757        // modify the reference target, which updates the entry in the reference origin cache
3758        assert!(
3759            el_fibex_element_ref
3760                .set_character_data("/PackageRenamed/EcuInstance2")
3761                .is_ok()
3762        );
3763        model
3764            .0
3765            .read()
3766            .reference_origins
3767            .get("/PackageRenamed/EcuInstance2")
3768            .unwrap();
3769        assert!(
3770            !model
3771                .0
3772                .read()
3773                .reference_origins
3774                .contains_key("/PackageRenamed/EcuInstance1")
3775        );
3776
3777        // can only set character data that are specified with ContentMode::Characters
3778        assert!(el_autosar.set_character_data("text").is_err());
3779
3780        // can't set a value that doesn't match the target spec
3781        assert!(el_short_name.set_character_data(0).is_err());
3782        assert!(el_short_name.set_character_data("").is_err());
3783
3784        // remove character data
3785        assert!(el_pnc_vector_length.remove_character_data().is_ok());
3786
3787        // remove the character data of a reference
3788        assert!(el_fibex_element_ref.remove_character_data().is_ok());
3789        assert!(
3790            !model
3791                .0
3792                .read()
3793                .reference_origins
3794                .contains_key("/PackageRenamed/EcuInstance2")
3795        );
3796
3797        // remove on an element whose character data has already been removed is not an error
3798        assert!(el_fibex_element_ref.remove_character_data().is_ok());
3799
3800        // can't remove SHORT-NAME
3801        assert!(el_short_name.remove_character_data().is_err());
3802
3803        // can't remove from elements which do not contain character data
3804        assert!(el_autosar.remove_character_data().is_err());
3805
3806        // slightly different behavior for the internal version that is used for locked elements
3807        assert!(
3808            el_autosar
3809                .0
3810                .write()
3811                .set_character_data(0, AutosarVersion::Autosar_00050)
3812                .is_err()
3813        );
3814        assert!(
3815            el_fibex_element_ref
3816                .0
3817                .write()
3818                .set_character_data(0, AutosarVersion::Autosar_00050)
3819                .is_err()
3820        );
3821
3822        // operation fails if the model is needed (e.g. reference or short name update), but the model has been deleted
3823        el_fibex_element_ref
3824            .set_character_data("/PackageRenamed/EcuInstance2")
3825            .unwrap();
3826        drop(model);
3827        assert!(
3828            el_fibex_element_ref
3829                .set_character_data("/PackageRenamed/EcuInstance1")
3830                .is_err()
3831        );
3832        assert!(el_fibex_element_ref.remove_character_data().is_err());
3833    }
3834
3835    #[test]
3836    fn mixed_character_content() {
3837        let model = AutosarModel::new();
3838        model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
3839        let el_ar_package = model
3840            .root_element()
3841            .create_sub_element(ElementName::ArPackages)
3842            .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))
3843            .unwrap();
3844        let el_desc = el_ar_package.create_sub_element(ElementName::Desc).unwrap();
3845        let el_l2 = el_desc.create_sub_element(ElementName::L2).unwrap();
3846
3847        // ok: add a character content item to a vaild element at a valid position
3848        el_l2.insert_character_content_item("descriptive text", 0).unwrap();
3849
3850        // ok: add an element to the mixed item as well
3851        el_l2.create_sub_element(ElementName::Br).unwrap();
3852
3853        // not ok: add a character content item to a valid element at an invalid position
3854        assert!(el_l2.insert_character_content_item("more text", 99).is_err());
3855
3856        // not ok: add a character content item to an invalid element
3857        assert!(el_desc.insert_character_content_item("text", 0).is_err());
3858
3859        // not ok: remove character content from an invalid position
3860        assert!(el_l2.remove_character_content_item(99).is_err());
3861
3862        // not ok: remove character content from an invalid element
3863        assert!(el_desc.remove_character_content_item(0).is_err());
3864
3865        // not ok: remove a sub-element
3866        assert!(el_l2.remove_character_content_item(1).is_err());
3867
3868        // ok: remove character content from a valid element at a valid position
3869        el_l2.remove_character_content_item(0).unwrap();
3870    }
3871
3872    #[test]
3873    fn get_sub_element() {
3874        let model = AutosarModel::new();
3875        model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
3876        let el_autosar = model.root_element();
3877        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
3878        let el_ar_package = el_ar_packages
3879            .create_named_sub_element(ElementName::ArPackage, "Package")
3880            .unwrap();
3881        let el_desc = el_ar_package.create_sub_element(ElementName::Desc).unwrap();
3882        let el_l2 = el_desc.create_sub_element(ElementName::L2).unwrap();
3883
3884        el_l2.insert_character_content_item("descriptive text", 0).unwrap();
3885        el_l2.create_sub_element(ElementName::Br).unwrap();
3886
3887        assert_eq!(
3888            el_autosar.get_sub_element(ElementName::ArPackages).unwrap(),
3889            el_ar_packages
3890        );
3891        assert!(el_autosar.get_sub_element(ElementName::Abs).is_none());
3892        assert!(el_l2.get_sub_element(ElementName::Br).is_some());
3893    }
3894
3895    #[test]
3896    fn get_or_create() {
3897        let model = AutosarModel::new();
3898        model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
3899        let el_autosar = model.root_element();
3900
3901        assert_eq!(el_autosar.sub_elements().count(), 0);
3902        let el_admin_data = el_autosar.get_or_create_sub_element(ElementName::AdminData).unwrap();
3903        let el_ar_packages = el_autosar.get_or_create_sub_element(ElementName::ArPackages).unwrap();
3904        let el_ar_packages2 = el_autosar.get_or_create_sub_element(ElementName::ArPackages).unwrap();
3905        assert_ne!(el_admin_data, el_ar_packages);
3906        assert_eq!(el_ar_packages, el_ar_packages2);
3907
3908        let el_ar_package = el_ar_packages
3909            .get_or_create_named_sub_element(ElementName::ArPackage, "Pkg")
3910            .unwrap();
3911        let el_ar_package2 = el_ar_packages
3912            .get_or_create_named_sub_element(ElementName::ArPackage, "Pkg2")
3913            .unwrap();
3914        let el_ar_package3 = el_ar_packages
3915            .get_or_create_named_sub_element(ElementName::ArPackage, "Pkg2")
3916            .unwrap();
3917        assert_ne!(el_ar_package, el_ar_package2);
3918        assert_eq!(el_ar_package2, el_ar_package3);
3919    }
3920
3921    #[test]
3922    fn serialize() {
3923        const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
3924<!--comment-->
3925<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">
3926  <AR-PACKAGES>
3927    <AR-PACKAGE>
3928      <SHORT-NAME>Pkg</SHORT-NAME>
3929      <DESC>
3930        <L-2 L="EN">Description<BR/>Description</L-2>
3931      </DESC>
3932    </AR-PACKAGE>
3933  </AR-PACKAGES>
3934</AUTOSAR>"#;
3935        let model = AutosarModel::new();
3936        model
3937            .load_buffer(FILEBUF.as_bytes(), OsString::from("test"), true)
3938            .unwrap();
3939        model.files().next().unwrap();
3940        let el_autosar = model.root_element();
3941        el_autosar.set_comment(Some("comment".to_string()));
3942
3943        let mut outstring = String::from(r#"<?xml version="1.0" encoding="utf-8"?>"#);
3944        el_autosar.serialize_internal(&mut outstring, 0, false, &None);
3945
3946        assert_eq!(FILEBUF, outstring);
3947    }
3948
3949    #[test]
3950    fn list_valid_sub_elements() {
3951        let model = AutosarModel::new();
3952        model.create_file("test.arxml", AutosarVersion::Autosar_4_3_0).unwrap();
3953        let el_autosar = model.root_element();
3954        let el_elements = el_autosar
3955            .create_sub_element(ElementName::ArPackages)
3956            .and_then(|el| el.create_named_sub_element(ElementName::ArPackage, "Package"))
3957            .and_then(|el| el.create_sub_element(ElementName::Elements))
3958            .unwrap();
3959        let result = el_elements.list_valid_sub_elements();
3960        assert!(!result.is_empty());
3961    }
3962
3963    #[test]
3964    fn check_version_compatibility() {
3965        const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
3966<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">
3967  <AR-PACKAGES>
3968    <AR-PACKAGE>
3969      <SHORT-NAME>Pkg</SHORT-NAME>
3970      <ELEMENTS>
3971        <ACL-OBJECT-SET UUID="012345">
3972          <SHORT-NAME BLUEPRINT-VALUE="xyz">AclObjectSet</SHORT-NAME>
3973          <DERIVED-FROM-BLUEPRINT-REFS>
3974            <DERIVED-FROM-BLUEPRINT-REF DEST="ABSTRACT-IMPLEMENTATION-DATA-TYPE">/invalid</DERIVED-FROM-BLUEPRINT-REF>
3975          </DERIVED-FROM-BLUEPRINT-REFS>
3976        </ACL-OBJECT-SET>
3977        <ADAPTIVE-APPLICATION-SW-COMPONENT-TYPE>
3978          <SHORT-NAME>AdaptiveApplicationSwComponentType</SHORT-NAME>
3979        </ADAPTIVE-APPLICATION-SW-COMPONENT-TYPE>
3980      </ELEMENTS>
3981    </AR-PACKAGE>
3982  </AR-PACKAGES>
3983</AUTOSAR>"#;
3984        let model = AutosarModel::new();
3985        let (file, _) = model
3986            .load_buffer(FILEBUF.as_bytes(), OsString::from("test"), true)
3987            .unwrap();
3988        model.files().next().unwrap();
3989        let el_autosar = model.root_element();
3990
3991        let (compat_errors, _) =
3992            el_autosar.check_version_compatibility(&file.downgrade(), AutosarVersion::Autosar_4_3_0);
3993        assert_eq!(compat_errors.len(), 3);
3994
3995        for ce in compat_errors {
3996            match ce {
3997                CompatibilityError::IncompatibleElement { element, .. } => {
3998                    assert_eq!(element.element_name(), ElementName::AdaptiveApplicationSwComponentType);
3999                }
4000                CompatibilityError::IncompatibleAttribute { element, attribute, .. } => {
4001                    assert_eq!(element.element_name(), ElementName::ShortName);
4002                    assert_eq!(attribute, AttributeName::BlueprintValue);
4003                }
4004                CompatibilityError::IncompatibleAttributeValue { element, attribute, .. } => {
4005                    assert_eq!(element.element_name(), ElementName::DerivedFromBlueprintRef);
4006                    assert_eq!(attribute, AttributeName::Dest);
4007                }
4008            }
4009        }
4010
4011        // regression test - CompuScales in CompuInternalToPhys was falsely detected as incompatible
4012        let model = AutosarModel::new();
4013        let file = model.create_file("filename", AutosarVersion::Autosar_00046).unwrap();
4014        model
4015            .root_element()
4016            .create_sub_element(ElementName::ArPackages)
4017            .and_then(|e| e.create_named_sub_element(ElementName::ArPackage, "Pkg"))
4018            .and_then(|e| e.create_sub_element(ElementName::Elements))
4019            .and_then(|e| e.create_named_sub_element(ElementName::CompuMethod, "CompuMethod"))
4020            .and_then(|e| e.create_sub_element(ElementName::CompuInternalToPhys))
4021            .and_then(|e| e.create_sub_element(ElementName::CompuScales))
4022            .and_then(|e| e.create_sub_element(ElementName::CompuScale))
4023            .unwrap();
4024        let (compat_errors, _) = file.check_version_compatibility(AutosarVersion::Autosar_4_3_0);
4025        assert!(compat_errors.is_empty());
4026    }
4027
4028    #[test]
4029    fn find_element_insert_pos() {
4030        let model = AutosarModel::new();
4031        model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
4032        let el_autosar = model.root_element();
4033        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
4034        let el_ar_package = el_ar_packages
4035            .create_named_sub_element(ElementName::ArPackage, "Pkg")
4036            .unwrap();
4037        let el_short_name = el_ar_package.get_sub_element(ElementName::ShortName).unwrap();
4038
4039        // find_element_insert_pos does not operat on CharacterData elements, e.g. SHORT-NAME
4040        assert!(
4041            el_short_name
4042                .0
4043                .read()
4044                .calc_element_insert_range(ElementName::Desc, AutosarVersion::Autosar_00050)
4045                .is_err()
4046        );
4047
4048        // find_element_insert_pos fails to find a place for a sequence element with multiplicity 0-1
4049        assert!(
4050            el_autosar
4051                .0
4052                .read()
4053                .calc_element_insert_range(ElementName::ArPackages, AutosarVersion::Autosar_00050)
4054                .is_err()
4055        );
4056    }
4057
4058    #[test]
4059    fn sort() {
4060        let model = AutosarModel::new();
4061        model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
4062        let el_autosar = model.root_element();
4063        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
4064        let el_ar_package1 = el_ar_packages
4065            .create_named_sub_element(ElementName::ArPackage, "Z")
4066            .unwrap();
4067        let el_ar_package2 = el_ar_packages
4068            .create_named_sub_element(ElementName::ArPackage, "A")
4069            .unwrap();
4070        let el_elements = el_ar_package1.create_sub_element(ElementName::Elements).unwrap();
4071        el_ar_package1.create_sub_element(ElementName::AdminData).unwrap();
4072        // create some bsw values to sort inside el_ar_package1 "Z"
4073        let el_emcv = el_elements
4074            .create_named_sub_element(ElementName::EcucModuleConfigurationValues, "Config")
4075            .unwrap();
4076        let el_containers = el_emcv.create_sub_element(ElementName::Containers).unwrap();
4077        let el_ecv = el_containers
4078            .create_named_sub_element(ElementName::EcucContainerValue, "ConfigValues")
4079            .unwrap();
4080        let el_paramvalues = el_ecv.create_sub_element(ElementName::ParameterValues).unwrap();
4081        // first bsw value
4082        let el_value1 = el_paramvalues
4083            .create_sub_element(ElementName::EcucNumericalParamValue)
4084            .unwrap();
4085        let el_defref1 = el_value1.create_sub_element(ElementName::DefinitionRef).unwrap();
4086        el_defref1
4087            .set_attribute(AttributeName::Dest, CharacterData::Enum(EnumItem::EcucBooleanParamDef))
4088            .unwrap();
4089        el_defref1.set_character_data("/DefRef_999").unwrap();
4090        // second bsw value
4091        let el_value2 = el_paramvalues
4092            .create_sub_element(ElementName::EcucNumericalParamValue)
4093            .unwrap();
4094        let el_defref2 = el_value2.create_sub_element(ElementName::DefinitionRef).unwrap();
4095        el_defref2
4096            .set_attribute(AttributeName::Dest, CharacterData::Enum(EnumItem::EcucBooleanParamDef))
4097            .unwrap();
4098        el_defref2.set_character_data("/DefRef_111").unwrap();
4099        // Create some misc value sto sort inside el_ar_package2 "A"
4100        let el_elements2 = el_ar_package2.create_sub_element(ElementName::Elements).unwrap();
4101        let el_system = el_elements2
4102            .create_named_sub_element(ElementName::System, "System")
4103            .unwrap();
4104        let el_fibex_elements = el_system.create_sub_element(ElementName::FibexElements).unwrap();
4105        let el_fibex_element1 = el_fibex_elements
4106            .create_sub_element(ElementName::FibexElementRefConditional)
4107            .unwrap();
4108        let el_fibex_element_ref1 = el_fibex_element1
4109            .create_sub_element(ElementName::FibexElementRef)
4110            .unwrap();
4111        el_fibex_element_ref1
4112            .set_attribute(AttributeName::Dest, CharacterData::Enum(EnumItem::ISignal))
4113            .unwrap();
4114        el_fibex_element_ref1.set_character_data("/ZZZZZ").unwrap();
4115        let el_fibex_element2 = el_fibex_elements
4116            .create_sub_element(ElementName::FibexElementRefConditional)
4117            .unwrap();
4118        let el_fibex_element_ref2 = el_fibex_element2
4119            .create_sub_element(ElementName::FibexElementRef)
4120            .unwrap();
4121        el_fibex_element_ref2
4122            .set_attribute(AttributeName::Dest, CharacterData::Enum(EnumItem::ISignal))
4123            .unwrap();
4124        el_fibex_element_ref2.set_character_data("/AAAAA").unwrap();
4125
4126        model.sort();
4127        // validate that identifiable elements have been sorted
4128        let mut iter = el_ar_packages.sub_elements();
4129        let item1 = iter.next().unwrap();
4130        let item2 = iter.next().unwrap();
4131        assert_eq!(item1.item_name().unwrap(), "A");
4132        assert_eq!(item2.item_name().unwrap(), "Z");
4133
4134        // validate that BSW parameter values have been sorted
4135        let mut iter = el_paramvalues.sub_elements();
4136        let item1 = iter.next().unwrap();
4137        let item2 = iter.next().unwrap();
4138        assert_eq!(item1, el_value2);
4139        assert_eq!(item2, el_value1);
4140
4141        // validate that the misc elements (FIBEX-ELEMENT-REF-CONDITIONAL) have been sorted
4142        let mut iter = el_fibex_elements.sub_elements();
4143        let item1 = iter.next().unwrap();
4144        let item2 = iter.next().unwrap();
4145        assert_eq!(item1, el_fibex_element2);
4146        assert_eq!(item2, el_fibex_element1);
4147    }
4148
4149    fn helper_create_bsw_subelem(
4150        el_subcontainers: &Element,
4151        short_name: &str,
4152        defref: &str,
4153    ) -> Result<Element, AutosarDataError> {
4154        let e = el_subcontainers.create_named_sub_element(ElementName::EcucContainerValue, short_name)?;
4155        let defrefelem = e.create_sub_element(ElementName::DefinitionRef)?;
4156        defrefelem.set_character_data(defref)?;
4157        Ok(e)
4158    }
4159
4160    fn helper_create_indexed_bsw_subelem(
4161        el_subcontainers: &Element,
4162        short_name: &str,
4163        indexstr: &str,
4164        defref: &str,
4165    ) -> Result<Element, AutosarDataError> {
4166        let e = helper_create_bsw_subelem(el_subcontainers, short_name, defref)?;
4167        let indexelem = e.create_sub_element(ElementName::Index)?;
4168        indexelem.set_character_data(indexstr)?;
4169        Ok(e)
4170    }
4171
4172    #[test]
4173    fn sort_bsw_elements() {
4174        let model = AutosarModel::new();
4175        model.create_file("test", AutosarVersion::LATEST).unwrap();
4176        let el_subcontainers = model
4177            .root_element()
4178            .create_sub_element(ElementName::ArPackages)
4179            .and_then(|ap| ap.create_named_sub_element(ElementName::ArPackage, "Pkg"))
4180            .and_then(|ap| ap.create_sub_element(ElementName::Elements))
4181            .and_then(|elems| elems.create_named_sub_element(ElementName::EcucModuleConfigurationValues, "Config"))
4182            .and_then(|emcv| emcv.create_sub_element(ElementName::Containers))
4183            .and_then(|c| c.create_named_sub_element(ElementName::EcucContainerValue, "ConfigValues"))
4184            .and_then(|ecv| ecv.create_sub_element(ElementName::SubContainers))
4185            .unwrap();
4186        let elem1 =
4187            helper_create_indexed_bsw_subelem(&el_subcontainers, "Aaa", "06", "/Defref/Container/Value").unwrap(); // idx 6
4188        let elem2 =
4189            helper_create_indexed_bsw_subelem(&el_subcontainers, "Bbb", "5", "/Defref/Container/Value").unwrap(); // idx 5
4190        let elem3 =
4191            helper_create_indexed_bsw_subelem(&el_subcontainers, "Bbb2", "5", "/Defref/Container/Value").unwrap(); // idx 5 duplicate
4192        let elem4 =
4193            helper_create_indexed_bsw_subelem(&el_subcontainers, "Ccc", "0X4", "/Defref/Container/Value").unwrap(); // idx 4
4194        let elem5 = helper_create_bsw_subelem(&el_subcontainers, "Zzz", "/Defref/Container/Value").unwrap();
4195        let elem6 =
4196            helper_create_indexed_bsw_subelem(&el_subcontainers, "Ddd", "0b1", "/Defref/Container/Value").unwrap(); // idx 1
4197        let elem7 =
4198            helper_create_indexed_bsw_subelem(&el_subcontainers, "Eee", "0x3", "/Defref/Container/Value").unwrap(); // idx 3
4199        let elem8 =
4200            helper_create_indexed_bsw_subelem(&el_subcontainers, "Fff", "0B10", "/Defref/Container/Value").unwrap(); // idx 2
4201        let elem9 =
4202            helper_create_indexed_bsw_subelem(&el_subcontainers, "Ggg", "0", "/Defref/Container/Value").unwrap(); // idx 0
4203
4204        let elem10 = helper_create_bsw_subelem(&el_subcontainers, "Mmm_0", "/Defref/Container/Value").unwrap();
4205        let elem11 = helper_create_bsw_subelem(&el_subcontainers, "Mmm_5", "/Defref/Container/Value").unwrap();
4206        let elem12 = helper_create_bsw_subelem(&el_subcontainers, "Mmm_10", "/Defref/Container/Value").unwrap();
4207        let elem13 = helper_create_bsw_subelem(&el_subcontainers, "Mmm_9", "/Defref/Container/Value").unwrap();
4208
4209        el_subcontainers.sort();
4210        assert_eq!(elem1.position().unwrap(), 7);
4211        assert_eq!(elem2.position().unwrap(), 5);
4212        assert_eq!(elem3.position().unwrap(), 6);
4213        assert_eq!(elem4.position().unwrap(), 4);
4214        assert_eq!(elem6.position().unwrap(), 1);
4215        assert_eq!(elem7.position().unwrap(), 3);
4216        assert_eq!(elem8.position().unwrap(), 2);
4217        assert_eq!(elem9.position().unwrap(), 0);
4218        // elements without indices are sorted behind the indexed elements
4219        assert_eq!(elem10.position().unwrap(), 8);
4220        assert_eq!(elem11.position().unwrap(), 9);
4221        assert_eq!(elem13.position().unwrap(), 10);
4222        assert_eq!(elem12.position().unwrap(), 11);
4223        assert_eq!(elem5.position().unwrap(), 12);
4224    }
4225
4226    #[test]
4227    fn file_membership() {
4228        let model = AutosarModel::new();
4229        let file1 = model.create_file("test_1", AutosarVersion::Autosar_00050).unwrap();
4230        let file2 = model.create_file("test_2", AutosarVersion::Autosar_00050).unwrap();
4231        let el_autosar = model.root_element();
4232        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
4233        let el_ar_package = el_ar_packages
4234            .create_named_sub_element(ElementName::ArPackage, "Pkg")
4235            .unwrap();
4236        el_ar_package.create_sub_element(ElementName::Elements).unwrap();
4237
4238        let fm: HashSet<WeakArxmlFile> = [file1.downgrade()].iter().cloned().collect();
4239        // setting the file membership of el_ar_packages should fail
4240        // its parent is not splittable, so this is not allowed
4241        el_ar_packages.set_file_membership(fm.clone());
4242        let (local, _) = el_ar_package.file_membership().unwrap();
4243        assert!(!local);
4244
4245        // setting the file membership of el_ar_package should succeed
4246        // this element is only part of file1, and is only serialized with file1
4247        el_ar_package.set_file_membership(fm.clone());
4248        let (local, fm2) = el_ar_package.file_membership().unwrap();
4249        assert!(local);
4250        assert_eq!(fm, fm2);
4251        let filetxt1 = file1.serialize().unwrap();
4252        let filetxt2 = file2.serialize().unwrap();
4253        assert_ne!(filetxt1, filetxt2);
4254
4255        // can't use a file from a different model in add_to_file / remove_from_file
4256        let model2 = AutosarModel::new();
4257        let model2_file = model2.create_file("file", AutosarVersion::LATEST).unwrap();
4258        assert!(el_ar_package.add_to_file(&model2_file).is_err());
4259        assert!(el_ar_package.remove_from_file(&model2_file).is_err());
4260
4261        // adding el_ar_package to file1 does nothing, since it is already present in this file
4262        el_ar_package.add_to_file(&file1).unwrap();
4263        let (local, fm3) = el_ar_package.file_membership().unwrap();
4264        assert!(local);
4265        assert_eq!(fm3.len(), 1);
4266
4267        // removing el_ar_package from file2 does nothing, it is not present in this file
4268        el_ar_package.remove_from_file(&file2).unwrap();
4269        let (local, fm3) = el_ar_package.file_membership().unwrap();
4270        assert!(local);
4271        assert_eq!(fm3.len(), 1);
4272
4273        // adding el_ar_package to file2 succeeds
4274        el_ar_package.add_to_file(&file2).unwrap();
4275        let (local, fm3) = el_ar_package.file_membership().unwrap();
4276        assert!(local);
4277        assert_eq!(fm3.len(), 2);
4278
4279        // removing el_ar_package from file1 and file2 causes it to be deleted
4280        assert!(el_ar_package.get_sub_element(ElementName::Elements).is_some());
4281        el_ar_package.remove_from_file(&file1).unwrap();
4282        el_ar_package.remove_from_file(&file2).unwrap();
4283        assert!(el_ar_package.get_sub_element(ElementName::Elements).is_none());
4284        assert!(el_ar_package.remove_from_file(&file2).is_err());
4285    }
4286
4287    #[test]
4288    fn comment() {
4289        let model = AutosarModel::new();
4290        model.create_file("test", AutosarVersion::LATEST).unwrap();
4291        let el_autosar = model.root_element();
4292
4293        // initially there is no comment
4294        assert!(el_autosar.comment().is_none());
4295
4296        // set and get a comment
4297        el_autosar.set_comment(Some("comment".to_string()));
4298        assert_eq!(el_autosar.comment().unwrap(), "comment");
4299
4300        // set a new comment containing "--" which is a forbidden sequence in XML comments
4301        el_autosar.set_comment(Some("comment--".to_string()));
4302        assert_eq!(el_autosar.comment().unwrap(), "comment__");
4303
4304        // remove the comment
4305        el_autosar.set_comment(None);
4306        assert!(el_autosar.comment().is_none());
4307    }
4308
4309    #[test]
4310    fn min_version() {
4311        let model = AutosarModel::new();
4312        let result = model.root_element().min_version();
4313        assert!(result.is_err());
4314
4315        model.create_file("test", AutosarVersion::LATEST).unwrap();
4316        let min_ver = model.root_element().min_version().unwrap();
4317        assert_eq!(min_ver, AutosarVersion::LATEST);
4318
4319        model.create_file("test2", AutosarVersion::Autosar_00042).unwrap();
4320        let min_ver = model.root_element().min_version().unwrap();
4321        assert_eq!(min_ver, AutosarVersion::Autosar_00042);
4322    }
4323
4324    #[test]
4325    fn traits() {
4326        let model = AutosarModel::new();
4327        model.create_file("test", AutosarVersion::LATEST).unwrap();
4328
4329        // traits of elements
4330        let el_autosar = model.root_element();
4331        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
4332
4333        let el_autosar_second_ref = el_autosar.clone();
4334        assert_eq!(el_autosar, el_autosar_second_ref);
4335        assert_eq!(format!("{el_autosar:?}"), format!("{el_autosar_second_ref:?}"));
4336        assert_ne!(el_autosar, el_ar_packages);
4337
4338        let weak1 = el_autosar.downgrade();
4339        let weak2 = el_autosar_second_ref.downgrade();
4340        assert_eq!(weak1, weak2);
4341        assert_eq!(format!("{weak1:?}"), format!("{weak2:?}"));
4342
4343        let mut hs = HashSet::new();
4344        hs.insert(el_autosar);
4345        hs.insert(el_ar_packages);
4346        // can't insert el_autosar_second_ref, it is already in the set
4347        assert!(!hs.insert(el_autosar_second_ref));
4348        assert_eq!(hs.len(), 2);
4349
4350        let mut hs2 = HashSet::new();
4351        hs2.insert(weak1);
4352        assert!(!hs2.insert(weak2));
4353        assert_eq!(hs2.len(), 1);
4354
4355        // traits of elementcontent
4356        let ec_elem = ElementContent::Element(model.root_element());
4357        assert_eq!(format!("{:?}", model.root_element()), format!("{ec_elem:?}"));
4358        assert_eq!(ec_elem.unwrap_element(), Some(model.root_element()));
4359        assert!(ec_elem.unwrap_cdata().is_none());
4360        let cdata = CharacterData::String("test".to_string());
4361        let ec_chars = ElementContent::CharacterData(cdata.clone());
4362        assert_eq!(format!("{cdata:?}"), format!("{ec_chars:?}"));
4363        assert_eq!(ec_chars.unwrap_cdata(), Some(cdata));
4364        assert!(ec_chars.unwrap_element().is_none());
4365    }
4366
4367    #[test]
4368    fn element_order() {
4369        let model = AutosarModel::new();
4370        let _file = model.create_file("test", AutosarVersion::LATEST).unwrap();
4371        let el_autosar = model.root_element();
4372        let el_elements = el_autosar
4373            .create_sub_element(ElementName::ArPackages)
4374            .unwrap()
4375            .create_named_sub_element(ElementName::ArPackage, "pkg")
4376            .unwrap()
4377            .create_sub_element(ElementName::Elements)
4378            .unwrap();
4379        let el_system = el_elements
4380            .create_named_sub_element(ElementName::System, "sys")
4381            .unwrap();
4382        let fibex_elements = el_system.create_sub_element(ElementName::FibexElements).unwrap();
4383
4384        let item1 = el_elements
4385            .create_named_sub_element(ElementName::ApplicationPrimitiveDataType, "adt_2")
4386            .unwrap();
4387        let item2 = el_elements
4388            .create_named_sub_element(ElementName::ApplicationPrimitiveDataType, "adt_10")
4389            .unwrap();
4390        let item3 = el_elements
4391            .create_named_sub_element(ElementName::ApplicationArrayDataType, "adt_12")
4392            .unwrap();
4393        // items 1 and 2 are sorted after separating the index from the name, so 10 comes after 2
4394        assert!(item1 < item2);
4395        // items 2 and 3 are sorted by the element type, so in this case the index does not matter
4396        assert!(item3 < item1);
4397
4398        let item4 = fibex_elements
4399            .create_sub_element(ElementName::FibexElementRefConditional)
4400            .unwrap();
4401        let item4_ref = item4.create_sub_element(ElementName::FibexElementRef).unwrap();
4402        item4_ref
4403            .set_attribute(AttributeName::Dest, CharacterData::Enum(EnumItem::ISignal))
4404            .unwrap();
4405        item4_ref.set_character_data("/aaa").unwrap();
4406
4407        let item5 = fibex_elements
4408            .create_sub_element(ElementName::FibexElementRefConditional)
4409            .unwrap();
4410        let item5_ref = item5.create_sub_element(ElementName::FibexElementRef).unwrap();
4411        item5_ref
4412            .set_attribute(AttributeName::Dest, CharacterData::Enum(EnumItem::ISignal))
4413            .unwrap();
4414        item5_ref.set_character_data("/bbb").unwrap();
4415
4416        // items 4 and 5 are sorted by the character data of the reference
4417        assert!(item4 < item5);
4418
4419        let item6 = fibex_elements
4420            .create_sub_element(ElementName::FibexElementRefConditional)
4421            .unwrap();
4422        let item6_ref = item6.create_sub_element(ElementName::FibexElementRef).unwrap();
4423        item6_ref
4424            .set_attribute(AttributeName::Dest, CharacterData::Enum(EnumItem::EcuInstance))
4425            .unwrap();
4426
4427        // items 4 and 6 are sorted by the DEST attribute of the reference
4428        assert!(item6 < item4);
4429
4430        let item7 = fibex_elements
4431            .create_sub_element(ElementName::FibexElementRefConditional)
4432            .unwrap();
4433        item7.create_sub_element(ElementName::FibexElementRef).unwrap();
4434
4435        // item7 is incomplete, lacking the DEST attribute so it is sorted last
4436        assert!(item7 > item6);
4437        assert!(item6 < item7);
4438    }
4439
4440    #[test]
4441    fn elements_dfs_with_max_depth() {
4442        const FILEBUF: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
4443        <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">
4444        <AR-PACKAGES>
4445          <AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
4446            <ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
4447              <SHORT-NAME>BswModuleValues</SHORT-NAME>
4448              <PARAMETER-VALUES>
4449                <ECUC-NUMERICAL-PARAM-VALUE>
4450                  <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
4451                </ECUC-NUMERICAL-PARAM-VALUE>
4452                <ECUC-NUMERICAL-PARAM-VALUE>
4453                  <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_B</DEFINITION-REF>
4454                </ECUC-NUMERICAL-PARAM-VALUE>
4455                <ECUC-NUMERICAL-PARAM-VALUE>
4456                  <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_C</DEFINITION-REF>
4457                </ECUC-NUMERICAL-PARAM-VALUE>
4458              </PARAMETER-VALUES>
4459            </ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
4460          </ELEMENTS></AR-PACKAGE>
4461          <AR-PACKAGE><SHORT-NAME>Pkg_B</SHORT-NAME></AR-PACKAGE>
4462          <AR-PACKAGE><SHORT-NAME>Pkg_C</SHORT-NAME></AR-PACKAGE>
4463        </AR-PACKAGES></AUTOSAR>"#.as_bytes();
4464        let model = AutosarModel::new();
4465        let (_, _) = model.load_buffer(FILEBUF, "test1", true).unwrap();
4466        let root_elem = model.root_element();
4467        let ar_packages_elem = root_elem.get_sub_element(ElementName::ArPackages).unwrap();
4468        let root_all_count = root_elem.elements_dfs().count();
4469        let ar_packages_all_count = ar_packages_elem.elements_dfs().count();
4470        assert_eq!(root_all_count, ar_packages_all_count + 1);
4471
4472        let root_lvl3_count = root_elem.elements_dfs_with_max_depth(3).count();
4473        let ar_packages_lvl2_count = ar_packages_elem.elements_dfs_with_max_depth(2).count();
4474        assert_eq!(root_lvl3_count, ar_packages_lvl2_count + 1);
4475
4476        root_elem
4477            .elements_dfs_with_max_depth(3)
4478            .skip(1)
4479            .zip(ar_packages_elem.elements_dfs_with_max_depth(2))
4480            .for_each(|((_, x), (_, y))| assert_eq!(x, y));
4481
4482        for elem in ar_packages_elem.elements_dfs_with_max_depth(2) {
4483            assert!(elem.0 <= 2);
4484        }
4485    }
4486
4487    #[test]
4488    fn parse_reference_bases() {
4489        // from issue #36
4490        const FILEBUF: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
4491<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_00048.xsd">
4492  <AR-PACKAGES>
4493    <AR-PACKAGE>
4494      <SHORT-NAME>My</SHORT-NAME>
4495      <AR-PACKAGES>
4496        <AR-PACKAGE>
4497          <SHORT-NAME>Lib</SHORT-NAME>
4498          <AR-PACKAGES>
4499            <AR-PACKAGE>
4500              <SHORT-NAME>Hierarchy</SHORT-NAME>
4501              <AR-PACKAGES>
4502                <AR-PACKAGE>
4503                  <SHORT-NAME>Units</SHORT-NAME>
4504                  <ELEMENTS>
4505                    <UNIT>
4506                      <SHORT-NAME>MyUnit</SHORT-NAME>
4507                      <FACTOR-SI-TO-UNIT>1</FACTOR-SI-TO-UNIT>
4508                      <OFFSET-SI-TO-UNIT>0</OFFSET-SI-TO-UNIT>
4509                    </UNIT>
4510                  </ELEMENTS>
4511                </AR-PACKAGE>
4512              </AR-PACKAGES>
4513            </AR-PACKAGE>
4514          </AR-PACKAGES>
4515        </AR-PACKAGE>
4516        <AR-PACKAGE>
4517          <SHORT-NAME>SWC</SHORT-NAME>
4518          <REFERENCE-BASES>
4519            <REFERENCE-BASE>
4520              <SHORT-LABEL>Units</SHORT-LABEL>
4521              <PACKAGE-REF DEST="AR-PACKAGE">/My/Lib/Hierarchy/Units</PACKAGE-REF>
4522            </REFERENCE-BASE>
4523          </REFERENCE-BASES>
4524          <AR-PACKAGES>
4525            <AR-PACKAGE>
4526              <SHORT-NAME>ApplicationDataTypes</SHORT-NAME>
4527              <ELEMENTS>
4528                <APPLICATION-PRIMITIVE-DATA-TYPE>
4529                  <SHORT-NAME>MyDataType</SHORT-NAME>
4530                  <CATEGORY>VALUE</CATEGORY>
4531                  <SW-DATA-DEF-PROPS>
4532                    <SW-DATA-DEF-PROPS-VARIANTS>
4533                      <SW-DATA-DEF-PROPS-CONDITIONAL>
4534                        <SW-CALIBRATION-ACCESS>NOT-ACCESSIBLE</SW-CALIBRATION-ACCESS>
4535                        <UNIT-REF DEST="UNIT" BASE="Units">MyUnit</UNIT-REF>
4536                      </SW-DATA-DEF-PROPS-CONDITIONAL>
4537                    </SW-DATA-DEF-PROPS-VARIANTS>
4538                  </SW-DATA-DEF-PROPS>
4539                </APPLICATION-PRIMITIVE-DATA-TYPE>
4540              </ELEMENTS>
4541            </AR-PACKAGE>
4542          </AR-PACKAGES>
4543        </AR-PACKAGE>
4544      </AR-PACKAGES>
4545    </AR-PACKAGE>
4546  </AR-PACKAGES>
4547</AUTOSAR>"#.as_bytes();
4548        let model = AutosarModel::new();
4549        let (_, _) = model.load_buffer(FILEBUF, "test1", true).unwrap();
4550
4551        let unit_elem = model.get_element_by_path("/My/Lib/Hierarchy/Units/MyUnit").unwrap();
4552        assert_eq!(unit_elem.element_name(), ElementName::Unit);
4553
4554        // verify that the reference base has been correctly parsed and cached
4555        assert_eq!(model.0.read().reference_bases.len(), 1);
4556        let locked_model = model.0.read();
4557        let (rb, rb_infos) = &locked_model.reference_bases.iter().next().unwrap();
4558        assert_eq!(*rb, "Units");
4559        assert_eq!(rb_infos.len(), 1);
4560        assert_eq!(rb_infos[0].package_ref, "/My/Lib/Hierarchy/Units");
4561        drop(locked_model);
4562
4563        // verify that we're correctly tracking incoming references to the unit element from the reference base
4564        let refs_to_unit = model.get_references_to(&unit_elem.path().unwrap());
4565        assert_eq!(refs_to_unit.len(), 1);
4566        let reference_elem = refs_to_unit[0].upgrade().unwrap();
4567        assert_eq!(reference_elem.element_name(), ElementName::UnitRef);
4568        assert!(reference_elem.attribute_value(AttributeName::Base).is_some());
4569
4570        // verify that we can navigate from the reference element to the target unit element
4571        let target = reference_elem.get_reference_target().unwrap();
4572        assert_eq!(target, unit_elem);
4573    }
4574
4575    #[test]
4576    fn modify_relative_ref() {
4577        const FILEBUF: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
4578<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_00048.xsd">
4579  <AR-PACKAGES>
4580    <AR-PACKAGE>
4581      <SHORT-NAME>First</SHORT-NAME>
4582      <AR-PACKAGES>
4583        <AR-PACKAGE>
4584          <SHORT-NAME>Second</SHORT-NAME>
4585          <ELEMENTS>
4586            <ECU-INSTANCE>
4587              <SHORT-NAME>Ecu</SHORT-NAME>
4588            </ECU-INSTANCE>
4589          </ELEMENTS>
4590        </AR-PACKAGE>
4591      </AR-PACKAGES>
4592    </AR-PACKAGE>
4593    <AR-PACKAGE>
4594      <SHORT-NAME>Ref</SHORT-NAME>
4595      <REFERENCE-BASES>
4596        <REFERENCE-BASE>
4597          <SHORT-LABEL>First</SHORT-LABEL>
4598          <PACKAGE-REF DEST="AR-PACKAGE">/First</PACKAGE-REF>
4599        </REFERENCE-BASE>
4600        <REFERENCE-BASE>
4601          <SHORT-LABEL>Second</SHORT-LABEL>
4602          <PACKAGE-REF DEST="AR-PACKAGE">/First/Second</PACKAGE-REF>
4603        </REFERENCE-BASE>
4604      </REFERENCE-BASES>
4605      <ELEMENTS>
4606        <SYSTEM>
4607          <SHORT-NAME>System</SHORT-NAME>
4608          <FIBEX-ELEMENTS>
4609            <FIBEX-ELEMENT-REF-CONDITIONAL>
4610              <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE" BASE="First">Second/Ecu</FIBEX-ELEMENT-REF>
4611            </FIBEX-ELEMENT-REF-CONDITIONAL>
4612          </FIBEX-ELEMENTS>
4613        </SYSTEM>
4614      </ELEMENTS>
4615    </AR-PACKAGE>
4616  </AR-PACKAGES>
4617</AUTOSAR>"#.as_bytes();
4618        let model = AutosarModel::new();
4619        let (_, _) = model.load_buffer(FILEBUF, "test1", true).unwrap();
4620
4621        let ref_elem = model
4622            .get_element_by_path("/Ref/System")
4623            .unwrap()
4624            .get_sub_element(ElementName::FibexElements)
4625            .unwrap()
4626            .get_sub_element(ElementName::FibexElementRefConditional)
4627            .unwrap()
4628            .get_sub_element(ElementName::FibexElementRef)
4629            .unwrap();
4630        let ecu_elem = ref_elem.get_reference_target().unwrap();
4631        assert_eq!(ecu_elem.element_name(), ElementName::EcuInstance);
4632        assert_eq!(model.get_references_to(&ecu_elem.path().unwrap()).len(), 1);
4633
4634        ref_elem.set_relative_reference_target(&ecu_elem, "Second").unwrap();
4635        assert_eq!(ref_elem.character_data().unwrap().string_value().unwrap(), "Ecu");
4636        assert_eq!(model.get_references_to(&ecu_elem.path().unwrap()).len(), 1);
4637    }
4638
4639    #[test]
4640    fn create_modify_delete_ref_bases() {
4641        let model = AutosarModel::new();
4642        model.create_file("test", AutosarVersion::LATEST).unwrap();
4643        let el_autosar = model.root_element();
4644        let el_ar_packages = el_autosar.create_sub_element(ElementName::ArPackages).unwrap();
4645        let el_ar_package = el_ar_packages
4646            .create_named_sub_element(ElementName::ArPackage, "Pkg")
4647            .unwrap();
4648        let el_ar_package2 = el_ar_packages
4649            .create_named_sub_element(ElementName::ArPackage, "Pkg2")
4650            .unwrap();
4651        let el_reference_bases = el_ar_package.create_sub_element(ElementName::ReferenceBases).unwrap();
4652        let el_reference_base = el_reference_bases
4653            .create_sub_element(ElementName::ReferenceBase)
4654            .unwrap();
4655        // no cached reference base, because label and packageref are missing
4656        assert_eq!(model.0.read().reference_bases.len(), 0);
4657        let el_short_label = el_reference_base.create_sub_element(ElementName::ShortLabel).unwrap();
4658        el_short_label.set_character_data("Units").unwrap();
4659        // no cached reference base, because packageref is missing
4660        assert_eq!(model.0.read().reference_bases.len(), 0);
4661        let el_package_ref = el_reference_base.create_sub_element(ElementName::PackageRef).unwrap();
4662        el_package_ref.set_reference_target(&el_ar_package2).unwrap();
4663        // cached reference base should be created now
4664        assert_eq!(model.0.read().reference_bases.len(), 1);
4665
4666        el_short_label.set_character_data("Modified").unwrap();
4667        // cached reference base should be updated with the new label
4668        let locked_model = model.0.read();
4669        let (rb, rb_infos) = &locked_model.reference_bases.iter().next().unwrap();
4670        assert_eq!(*rb, "Modified");
4671        assert_eq!(rb_infos[0].package_ref, "/Pkg2");
4672        assert_eq!(rb_infos.len(), 1);
4673        drop(locked_model);
4674
4675        el_package_ref.set_reference_target(&el_ar_package).unwrap();
4676        // cached reference base should be updated with the new package ref
4677        let locked_model = model.0.read();
4678        let (rb, rb_infos) = &locked_model.reference_bases.iter().next().unwrap();
4679        assert_eq!(*rb, "Modified");
4680        assert_eq!(rb_infos[0].package_ref, "/Pkg");
4681        drop(locked_model);
4682
4683        el_reference_base.remove_sub_element(el_package_ref).unwrap();
4684        // cached reference base should be removed again
4685        assert_eq!(model.0.read().reference_bases.len(), 0);
4686        // create it again
4687        let el_package_ref = el_reference_base.create_sub_element(ElementName::PackageRef).unwrap();
4688        el_package_ref.set_reference_target(&el_ar_package2).unwrap();
4689        assert_eq!(model.0.read().reference_bases.len(), 1);
4690        // remove the SHORT-LABEL
4691        el_reference_base.remove_sub_element(el_short_label).unwrap();
4692        // cached reference base should be removed again, because the label is missing
4693        assert_eq!(model.0.read().reference_bases.len(), 0);
4694        let el_short_label = el_reference_base.create_sub_element(ElementName::ShortLabel).unwrap();
4695        el_short_label.set_character_data("Units").unwrap();
4696        // cached reference base should be created again
4697        assert_eq!(model.0.read().reference_bases.len(), 1);
4698
4699        el_reference_bases.remove_sub_element(el_reference_base).unwrap();
4700        // cached reference base should be removed again, because the reference base itself is removed
4701        assert_eq!(model.0.read().reference_bases.len(), 0);
4702    }
4703
4704    #[test]
4705    fn rename_reference_base() {
4706        let model = AutosarModel::new();
4707        model.create_file("test", AutosarVersion::LATEST).unwrap();
4708
4709        let el_ar_packages = model
4710            .root_element()
4711            .create_sub_element(ElementName::ArPackages)
4712            .unwrap();
4713        let owner1 = el_ar_packages
4714            .create_named_sub_element(ElementName::ArPackage, "Owner1")
4715            .unwrap();
4716        let owner2 = el_ar_packages
4717            .create_named_sub_element(ElementName::ArPackage, "Owner2")
4718            .unwrap();
4719        let target1 = el_ar_packages
4720            .create_named_sub_element(ElementName::ArPackage, "Target1")
4721            .unwrap();
4722        let target2 = el_ar_packages
4723            .create_named_sub_element(ElementName::ArPackage, "Target2")
4724            .unwrap();
4725
4726        let short_label_1 = owner1
4727            .create_sub_element(ElementName::ReferenceBases)
4728            .and_then(|e| e.create_sub_element(ElementName::ReferenceBase))
4729            .and_then(|e| {
4730                e.create_sub_element(ElementName::ShortLabel)
4731                    .and_then(|l| l.set_character_data("Shared").map(|_| l))
4732            })
4733            .unwrap();
4734        let owner1_reference_base = owner1
4735            .get_sub_element(ElementName::ReferenceBases)
4736            .and_then(|e| e.get_sub_element(ElementName::ReferenceBase))
4737            .unwrap();
4738        owner1_reference_base
4739            .create_sub_element(ElementName::PackageRef)
4740            .and_then(|p| p.set_reference_target(&target1))
4741            .unwrap();
4742
4743        owner2
4744            .create_sub_element(ElementName::ReferenceBases)
4745            .and_then(|e| e.create_sub_element(ElementName::ReferenceBase))
4746            .and_then(|e| {
4747                e.create_sub_element(ElementName::ShortLabel)
4748                    .and_then(|l| l.set_character_data("Shared").map(|_| l))
4749            })
4750            .unwrap();
4751        let owner2_reference_base = owner2
4752            .get_sub_element(ElementName::ReferenceBases)
4753            .and_then(|e| e.get_sub_element(ElementName::ReferenceBase))
4754            .unwrap();
4755        owner2_reference_base
4756            .create_sub_element(ElementName::PackageRef)
4757            .and_then(|p| p.set_reference_target(&target2))
4758            .unwrap();
4759
4760        assert_eq!(model.0.read().reference_bases.get("Shared").unwrap().len(), 2);
4761
4762        short_label_1.set_character_data("Renamed").unwrap();
4763
4764        let data = model.0.read();
4765        let shared_entries = data.reference_bases.get("Shared").unwrap();
4766        assert_eq!(shared_entries.len(), 1);
4767        assert_eq!(shared_entries[0].owner_package_path, "/Owner2");
4768        assert_eq!(shared_entries[0].package_ref, "/Target2");
4769
4770        let renamed_entries = data.reference_bases.get("Renamed").unwrap();
4771        assert_eq!(renamed_entries.len(), 1);
4772        assert_eq!(renamed_entries[0].owner_package_path, "/Owner1");
4773        assert_eq!(renamed_entries[0].package_ref, "/Target1");
4774    }
4775
4776    #[test]
4777    fn changing_base_attribute() {
4778        let model = AutosarModel::new();
4779        model.create_file("test", AutosarVersion::LATEST).unwrap();
4780
4781        let el_ar_packages = model
4782            .root_element()
4783            .create_sub_element(ElementName::ArPackages)
4784            .unwrap();
4785        let base_a = el_ar_packages
4786            .create_named_sub_element(ElementName::ArPackage, "BaseA")
4787            .unwrap();
4788        let base_b = el_ar_packages
4789            .create_named_sub_element(ElementName::ArPackage, "BaseB")
4790            .unwrap();
4791        let ref_pkg = el_ar_packages
4792            .create_named_sub_element(ElementName::ArPackage, "RefPkg")
4793            .unwrap();
4794
4795        let ecu_a = base_a
4796            .create_sub_element(ElementName::Elements)
4797            .and_then(|e| e.create_named_sub_element(ElementName::EcuInstance, "Ecu"))
4798            .unwrap();
4799        let _ecu_b = base_b
4800            .create_sub_element(ElementName::Elements)
4801            .and_then(|e| e.create_named_sub_element(ElementName::EcuInstance, "Ecu"))
4802            .unwrap();
4803
4804        let ref_bases = ref_pkg.create_sub_element(ElementName::ReferenceBases).unwrap();
4805        let rb_a = ref_bases.create_sub_element(ElementName::ReferenceBase).unwrap();
4806        rb_a.create_sub_element(ElementName::ShortLabel)
4807            .and_then(|e| e.set_character_data("A"))
4808            .unwrap();
4809        rb_a.create_sub_element(ElementName::PackageRef)
4810            .and_then(|e| e.set_reference_target(&base_a))
4811            .unwrap();
4812
4813        let rb_b = ref_bases.create_sub_element(ElementName::ReferenceBase).unwrap();
4814        rb_b.create_sub_element(ElementName::ShortLabel)
4815            .and_then(|e| e.set_character_data("B"))
4816            .unwrap();
4817        rb_b.create_sub_element(ElementName::PackageRef)
4818            .and_then(|e| e.set_reference_target(&base_b))
4819            .unwrap();
4820
4821        let ref_elem = ref_pkg
4822            .create_sub_element(ElementName::Elements)
4823            .and_then(|e| e.create_named_sub_element(ElementName::System, "Sys"))
4824            .and_then(|e| e.create_sub_element(ElementName::FibexElements))
4825            .and_then(|e| e.create_sub_element(ElementName::FibexElementRefConditional))
4826            .and_then(|e| e.create_sub_element(ElementName::FibexElementRef))
4827            .unwrap();
4828        ref_elem.set_relative_reference_target(&ecu_a, "A").unwrap();
4829
4830        ref_elem.set_attribute_string(AttributeName::Base, "B").unwrap();
4831
4832        let origins = model.0.read().relative_reference_origins.get("Ecu").unwrap().clone();
4833        let ref_origin = ref_elem.downgrade();
4834        assert!(origins.iter().any(|(elem, base)| *elem == ref_origin && base == "B"));
4835    }
4836}