Skip to main content

opcua/server/address_space/
references.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2022 Adam Lock
4
5use std::collections::{HashMap, HashSet};
6
7use crate::types::*;
8
9/// The `NodeId` is the target node. The reference is held in a list by the source node.
10/// The target node does not need to exist.
11#[derive(PartialEq, Eq, Clone, Debug, Hash)]
12pub struct Reference {
13    pub reference_type: NodeId,
14    pub target_node: NodeId,
15}
16
17impl Reference {
18    pub fn new<T>(reference_type: T, target_node: NodeId) -> Reference
19    where
20        T: Into<NodeId>,
21    {
22        Reference {
23            reference_type: reference_type.into(),
24            target_node,
25        }
26    }
27}
28
29#[derive(Debug, Copy, Clone)]
30pub enum ReferenceDirection {
31    Forward,
32    Inverse,
33}
34
35pub struct References {
36    /// The references map contains all forward references, i.e. the key is the node that has
37    /// a reference to one or more other nodes.
38    references_map: HashMap<NodeId, Vec<Reference>>,
39    /// The referenced by map allows a reverse lookup, to find what nodes this node is referenced
40    /// by. It is not the same as an inverse reference. A node may be referenced one or more
41    /// times by the other node.
42    referenced_by_map: HashMap<NodeId, HashSet<NodeId>>,
43}
44
45impl Default for References {
46    fn default() -> Self {
47        Self {
48            references_map: HashMap::with_capacity(2000),
49            referenced_by_map: HashMap::with_capacity(2000),
50        }
51    }
52}
53
54impl References {
55    /// Inserts a single reference into the map.
56    pub fn insert<T>(
57        &mut self,
58        source_node: &NodeId,
59        references: &[(&NodeId, &T, ReferenceDirection)],
60    ) where
61        T: Into<NodeId> + Clone,
62    {
63        references.iter().for_each(|r| {
64            // An inverse reference will flip the nodes around
65            match r.2 {
66                ReferenceDirection::Forward => self.insert_reference(source_node, r.0, r.1),
67                ReferenceDirection::Inverse => self.insert_reference(r.0, source_node, r.1),
68            };
69        });
70    }
71
72    /// For testing purposes, tests if the node exists anywhere in either direction. This test
73    /// just scans everything, looking for any mention of the node. Useful for tests which delete
74    /// nodes and expect them to be completely gone.
75    #[cfg(test)]
76    pub fn reference_to_node_exists(&self, node_id: &NodeId) -> bool {
77        if self.referenced_by_map.contains_key(node_id) {
78            debug!("Node {} is a key in references_to_map", node_id);
79            true
80        } else if self.references_map.contains_key(node_id) {
81            debug!("Node {} is a key in references_from_map", node_id);
82            true
83        } else if self
84            .references_map
85            .iter()
86            .find(|(k, v)| {
87                if let Some(r) = v.iter().find(|r| r.target_node == *node_id) {
88                    debug!(
89                        "Node {} is a value in references_from_map[{}, reference = {:?}",
90                        node_id, k, r
91                    );
92                    true
93                } else {
94                    false
95                }
96            })
97            .is_some()
98        {
99            true
100        } else if self
101            .referenced_by_map
102            .iter()
103            .find(|(k, v)| {
104                if v.contains(node_id) {
105                    debug!(
106                        "Node {} is a value in referenced_by_map, key {}",
107                        node_id, k
108                    );
109                    true
110                } else {
111                    false
112                }
113            })
114            .is_some()
115        {
116            true
117        } else {
118            false
119        }
120    }
121
122    pub fn insert_reference<T>(
123        &mut self,
124        source_node: &NodeId,
125        target_node: &NodeId,
126        reference_type: &T,
127    ) where
128        T: Into<NodeId> + Clone,
129    {
130        if source_node == target_node {
131            panic!(
132                "Node id from == node id to {}, self reference is not allowed",
133                source_node
134            );
135        }
136
137        let reference_type: NodeId = reference_type.clone().into();
138        let reference = Reference::new(reference_type, target_node.clone());
139
140        if let Some(ref mut references) = self.references_map.get_mut(source_node) {
141            // Duplicates are possible from the machine generated code, so skip dupes
142            if !references.contains(&reference) {
143                references.push(reference);
144            }
145        } else {
146            // Some nodes will have more than one reference, so save some reallocs by reserving
147            // space for some more.
148            let mut references = Vec::with_capacity(8);
149            references.push(reference);
150            self.references_map.insert(source_node.clone(), references);
151        }
152
153        // Add a reverse lookup reference
154        if let Some(ref mut lookup_set) = self.referenced_by_map.get_mut(target_node) {
155            lookup_set.insert(source_node.clone());
156        } else {
157            let mut lookup_set = HashSet::new();
158            lookup_set.insert(source_node.clone());
159            self.referenced_by_map
160                .insert(target_node.clone(), lookup_set);
161        }
162    }
163
164    /// Inserts references into the map.
165    pub fn insert_references<T>(&mut self, references: &[(&NodeId, &NodeId, &T)])
166    where
167        T: Into<NodeId> + Clone,
168    {
169        references.iter().for_each(|r| {
170            self.insert_reference(r.0, r.1, r.2);
171        });
172    }
173
174    fn remove_node_from_referenced_nodes(
175        &mut self,
176        nodes_to_check: HashSet<NodeId>,
177        node_to_remove: &NodeId,
178    ) {
179        nodes_to_check.into_iter().for_each(|node_to_check| {
180            // Removes any references that refer from the node to check back to the node to remove
181            let remove_entry =
182                if let Some(ref mut references) = self.references_map.get_mut(&node_to_check) {
183                    references.retain(|r| r.target_node != *node_to_remove);
184                    references.is_empty()
185                } else {
186                    false
187                };
188            if remove_entry {
189                self.references_map.remove(&node_to_check);
190            }
191            // Remove lookup that refer from the node to check back to the node to remove
192            let remove_lookup_map =
193                if let Some(ref mut lookup_map) = self.referenced_by_map.get_mut(&node_to_check) {
194                    lookup_map.remove(node_to_remove);
195                    lookup_map.is_empty()
196                } else {
197                    false
198                };
199            if remove_lookup_map {
200                self.referenced_by_map.remove(&node_to_check);
201            }
202        });
203    }
204
205    /// Deletes a matching references between one node and the target node of the specified
206    /// reference type. The function returns true if the reference was found and deleted.
207    pub fn delete_reference<T>(
208        &mut self,
209        source_node: &NodeId,
210        target_node: &NodeId,
211        reference_type: T,
212    ) -> bool
213    where
214        T: Into<NodeId>,
215    {
216        let reference_type = reference_type.into();
217
218        let mut deleted = false;
219        let mut remove_entry = false;
220        // Remove the source node reference
221        if let Some(references) = self.references_map.get_mut(source_node) {
222            // Make a set of all the nodes that this node references
223            let other_nodes_before = references
224                .iter()
225                .map(|r| r.target_node.clone())
226                .collect::<HashSet<NodeId>>();
227            // Delete a reference
228            references.retain(|r| {
229                if r.reference_type == reference_type && r.target_node == *target_node {
230                    deleted = true;
231                    false
232                } else {
233                    true
234                }
235            });
236            if references.is_empty() {
237                remove_entry = true;
238            }
239
240            // Make a set of all nodes that this node references (after removal)
241            let other_nodes_after = references
242                .iter()
243                .map(|r| r.target_node.clone())
244                .collect::<HashSet<NodeId>>();
245
246            // If nodes are no longer referenced, then the ones that were removed must also have their
247            // references changed.
248            let difference = other_nodes_before
249                .difference(&other_nodes_after)
250                .cloned()
251                .collect::<HashSet<NodeId>>();
252            if !difference.is_empty() {
253                self.remove_node_from_referenced_nodes(difference, source_node);
254            }
255        }
256        if remove_entry {
257            self.references_map.remove(source_node);
258        }
259
260        deleted
261    }
262
263    /// Deletes all references to the node.
264    pub fn delete_node_references(&mut self, source_node: &NodeId) -> bool {
265        let deleted_references = if let Some(references) = self.references_map.remove(source_node) {
266            // Deleted every reference from the node, and clean up the reverse lookup map
267            let nodes_referenced = references
268                .iter()
269                .map(|r| r.target_node.clone())
270                .collect::<HashSet<NodeId>>();
271            self.remove_node_from_referenced_nodes(nodes_referenced, source_node);
272            true
273        } else {
274            false
275        };
276
277        let deleted_lookups = if let Some(lookup_map) = self.referenced_by_map.remove(source_node) {
278            self.remove_node_from_referenced_nodes(lookup_map, source_node);
279            true
280        } else {
281            false
282        };
283
284        deleted_references || deleted_lookups
285    }
286
287    /// Test if a reference relationship exists between one node and another node
288    pub fn has_reference<T>(
289        &self,
290        source_node: &NodeId,
291        target_node: &NodeId,
292        reference_type: T,
293    ) -> bool
294    where
295        T: Into<NodeId>,
296    {
297        if let Some(references) = self.references_map.get(source_node) {
298            let reference = Reference::new(reference_type.into(), target_node.clone());
299            references.contains(&reference)
300        } else {
301            false
302        }
303    }
304
305    /// Finds forward references from the node
306    pub fn find_references<T>(
307        &self,
308        source_node: &NodeId,
309        reference_filter: Option<(T, bool)>,
310    ) -> Option<Vec<Reference>>
311    where
312        T: Into<NodeId> + Clone,
313    {
314        if let Some(node_references) = self.references_map.get(source_node) {
315            let result = self.filter_references_by_type(node_references, &reference_filter);
316            if result.is_empty() {
317                None
318            } else {
319                Some(result)
320            }
321        } else {
322            None
323        }
324    }
325
326    /// Returns inverse references for the target node, i.e if there are references where
327    /// `Reference.target_node` matches the supplied target node then return references
328    /// where `Reference.target_node` is the source node.
329    pub fn find_inverse_references<T>(
330        &self,
331        target_node: &NodeId,
332        reference_filter: Option<(T, bool)>,
333    ) -> Option<Vec<Reference>>
334    where
335        T: Into<NodeId> + Clone,
336    {
337        if let Some(lookup_map) = self.referenced_by_map.get(target_node) {
338            // Iterate all nodes that reference this node, collecting their references
339            let mut result = Vec::with_capacity(16);
340            lookup_map.iter().for_each(|source_node| {
341                if let Some(references) = self.references_map.get(source_node) {
342                    let references = references
343                        .iter()
344                        .filter(|r| r.target_node == *target_node)
345                        .map(|r| Reference {
346                            reference_type: r.reference_type.clone(),
347                            target_node: source_node.clone(),
348                        })
349                        .collect::<Vec<Reference>>();
350                    let mut references =
351                        self.filter_references_by_type(&references, &reference_filter);
352                    if !references.is_empty() {
353                        result.append(&mut references);
354                    }
355                }
356            });
357            if result.is_empty() {
358                None
359            } else {
360                Some(result)
361            }
362        } else {
363            None
364        }
365    }
366
367    fn filter_references_by_type<T>(
368        &self,
369        references: &[Reference],
370        reference_filter: &Option<(T, bool)>,
371    ) -> Vec<Reference>
372    where
373        T: Into<NodeId> + Clone,
374    {
375        match reference_filter {
376            None => references.to_owned(),
377            Some((reference_type_id, include_subtypes)) => {
378                let reference_type_id = reference_type_id.clone().into();
379                references
380                    .iter()
381                    .filter(|r| {
382                        self.reference_type_matches(
383                            &reference_type_id,
384                            &r.reference_type,
385                            *include_subtypes,
386                        )
387                    })
388                    .cloned()
389                    .collect::<Vec<Reference>>()
390            }
391        }
392    }
393
394    /// Find references optionally to and/or from the specified node id. The browse direction
395    /// indicates the desired direction, or both. The reference filter indicates if only references
396    /// of a certain type (including sub types) should be fetched.
397    pub fn find_references_by_direction<T>(
398        &self,
399        node: &NodeId,
400        browse_direction: BrowseDirection,
401        reference_filter: Option<(T, bool)>,
402    ) -> (Vec<Reference>, usize)
403    where
404        T: Into<NodeId> + Clone,
405    {
406        let mut references = Vec::new();
407        let inverse_ref_idx: usize;
408        match browse_direction {
409            BrowseDirection::Forward => {
410                if let Some(mut forward_references) = self.find_references(node, reference_filter) {
411                    references.append(&mut forward_references);
412                }
413                inverse_ref_idx = references.len();
414            }
415            BrowseDirection::Inverse => {
416                inverse_ref_idx = 0;
417                if let Some(mut inverse_references) =
418                    self.find_inverse_references(node, reference_filter)
419                {
420                    references.append(&mut inverse_references);
421                }
422            }
423            BrowseDirection::Both => {
424                let reference_filter: Option<(NodeId, bool)> =
425                    reference_filter.map(|(reference_type, include_subtypes)| {
426                        (reference_type.into(), include_subtypes)
427                    });
428                if let Some(mut forward_references) =
429                    self.find_references(node, reference_filter.clone())
430                {
431                    references.append(&mut forward_references);
432                }
433                inverse_ref_idx = references.len();
434                if let Some(mut inverse_references) =
435                    self.find_inverse_references(node, reference_filter)
436                {
437                    references.append(&mut inverse_references);
438                }
439            }
440            BrowseDirection::Invalid => {
441                error!("BrowseDirection::Invalid passed to find_references_by_direction");
442                inverse_ref_idx = 0;
443            }
444        }
445        (references, inverse_ref_idx)
446    }
447
448    /// Test if a reference type matches another reference type which is potentially a subtype.
449    /// If `include_subtypes` is set to true, the function will test if the subttype
450    /// for a match.
451    pub fn reference_type_matches(
452        &self,
453        ref_type: &NodeId,
454        ref_subtype: &NodeId,
455        include_subtypes: bool,
456    ) -> bool {
457        if ref_type == ref_subtype {
458            true
459        } else if include_subtypes {
460            let has_subtype: NodeId = ReferenceTypeId::HasSubtype.into();
461
462            let mut stack = Vec::with_capacity(20);
463            stack.push(ref_type.clone());
464
465            // Search every type and subtype until exhausted
466            let mut found = false;
467            while let Some(current) = stack.pop() {
468                // Get all references to subtypes
469                if *ref_subtype == current {
470                    found = true;
471                    break;
472                } else if let Some(references) = self.references_map.get(&current) {
473                    let mut subtypes = references
474                        .iter()
475                        .filter(|r| r.reference_type == has_subtype)
476                        .map(|r| r.target_node.clone())
477                        .collect::<Vec<NodeId>>();
478                    if subtypes.contains(ref_subtype) {
479                        found = true;
480                        break;
481                    }
482                    stack.append(&mut subtypes);
483                }
484            }
485            found
486        } else {
487            false
488        }
489    }
490
491    pub fn get_type_id(&self, node: &NodeId) -> Option<NodeId> {
492        if let Some(references) = self.references_map.get(node) {
493            let has_type_definition_id = ReferenceTypeId::HasTypeDefinition.into();
494            references
495                .iter()
496                .find(|r| r.reference_type == has_type_definition_id)
497                .map(|reference| reference.target_node.clone())
498        } else {
499            None
500        }
501    }
502}