opcua/server/address_space/
references.rs1use std::collections::{HashMap, HashSet};
6
7use crate::types::*;
8
9#[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 references_map: HashMap<NodeId, Vec<Reference>>,
39 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 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 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 #[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 if !references.contains(&reference) {
143 references.push(reference);
144 }
145 } else {
146 let mut references = Vec::with_capacity(8);
149 references.push(reference);
150 self.references_map.insert(source_node.clone(), references);
151 }
152
153 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 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 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 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 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 if let Some(references) = self.references_map.get_mut(source_node) {
222 let other_nodes_before = references
224 .iter()
225 .map(|r| r.target_node.clone())
226 .collect::<HashSet<NodeId>>();
227 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 let other_nodes_after = references
242 .iter()
243 .map(|r| r.target_node.clone())
244 .collect::<HashSet<NodeId>>();
245
246 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 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 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 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 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 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 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 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 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 let mut found = false;
467 while let Some(current) = stack.pop() {
468 if *ref_subtype == current {
470 found = true;
471 break;
472 } else if let Some(references) = self.references_map.get(¤t) {
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}