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