use std::{
borrow::Borrow,
collections::{HashMap, HashSet},
};
use crate::NamespaceMap;
use opcua_types::{
DataTypeId, NodeClass, NodeId, ObjectTypeId, QualifiedName, ReferenceTypeId, VariableTypeId,
};
#[derive(PartialEq, Eq, Hash, Clone)]
struct TypePropertyKey {
path: Vec<QualifiedName>,
}
impl Borrow<[QualifiedName]> for TypePropertyKey {
fn borrow(&self) -> &[QualifiedName] {
&self.path
}
}
#[derive(Clone, Debug)]
pub struct TypeProperty {
pub node_id: NodeId,
pub node_class: NodeClass,
}
#[derive(Clone, Debug)]
pub struct TypePropertyInverseRef {
pub type_id: NodeId,
pub path: Vec<QualifiedName>,
}
#[derive(Default, Clone)]
pub struct DefaultTypeTree {
nodes: HashMap<NodeId, NodeClass>,
subtypes_by_source: HashMap<NodeId, HashSet<NodeId>>,
subtypes_by_target: HashMap<NodeId, NodeId>,
property_to_type: HashMap<NodeId, TypePropertyInverseRef>,
type_properties: HashMap<NodeId, HashMap<TypePropertyKey, TypeProperty>>,
namespaces: NamespaceMap,
}
#[derive(Clone, Debug)]
pub enum TypeTreeNode<'a> {
Type(NodeClass),
Property(&'a TypePropertyInverseRef),
}
pub trait TypeTree {
fn is_subtype_of(&self, child: &NodeId, ancestor: &NodeId) -> bool;
fn get_node<'a>(&'a self, node: &NodeId) -> Option<TypeTreeNode<'a>>;
fn get(&self, node: &NodeId) -> Option<NodeClass>;
fn find_type_prop_by_browse_path(
&self,
type_id: &NodeId,
path: &[QualifiedName],
) -> Option<&TypeProperty>;
fn get_supertype<'a>(&'a self, node: &NodeId) -> Option<&'a NodeId>;
fn namespaces(&self) -> &NamespaceMap;
}
impl TypeTree for DefaultTypeTree {
fn is_subtype_of(&self, child: &NodeId, ancestor: &NodeId) -> bool {
let mut node = child;
loop {
if node == ancestor {
break true;
}
let Some(class) = self.nodes.get(node) else {
break false;
};
if !matches!(
class,
NodeClass::DataType
| NodeClass::ObjectType
| NodeClass::ReferenceType
| NodeClass::VariableType
) {
break false;
}
match self.subtypes_by_target.get(node) {
Some(n) => node = n,
None => break false,
}
}
}
fn get_node<'a>(&'a self, node: &NodeId) -> Option<TypeTreeNode<'a>> {
if let Some(n) = self.nodes.get(node) {
return Some(TypeTreeNode::Type(*n));
}
if let Some(p) = self.property_to_type.get(node) {
return Some(TypeTreeNode::Property(p));
}
None
}
fn get(&self, node: &NodeId) -> Option<NodeClass> {
self.nodes.get(node).cloned()
}
fn find_type_prop_by_browse_path(
&self,
type_id: &NodeId,
path: &[QualifiedName],
) -> Option<&TypeProperty> {
self.type_properties.get(type_id).and_then(|p| p.get(path))
}
fn get_supertype<'a>(&'a self, node: &NodeId) -> Option<&'a NodeId> {
self.subtypes_by_target.get(node)
}
fn namespaces(&self) -> &NamespaceMap {
&self.namespaces
}
}
impl DefaultTypeTree {
pub fn new() -> Self {
let mut type_tree = Self {
nodes: HashMap::new(),
subtypes_by_source: HashMap::new(),
subtypes_by_target: HashMap::new(),
type_properties: HashMap::new(),
property_to_type: HashMap::new(),
namespaces: NamespaceMap::new(),
};
type_tree
.namespaces
.add_namespace("http://opcfoundation.org/UA/");
type_tree
.nodes
.insert(ObjectTypeId::BaseObjectType.into(), NodeClass::ObjectType);
type_tree
.nodes
.insert(ReferenceTypeId::References.into(), NodeClass::ReferenceType);
type_tree.nodes.insert(
VariableTypeId::BaseVariableType.into(),
NodeClass::VariableType,
);
type_tree
.nodes
.insert(DataTypeId::BaseDataType.into(), NodeClass::DataType);
type_tree
}
pub fn add_type_node(&mut self, id: &NodeId, parent: &NodeId, node_class: NodeClass) {
self.nodes.insert(id.clone(), node_class);
self.subtypes_by_source
.entry(parent.clone())
.or_default()
.insert(id.clone());
self.subtypes_by_target.insert(id.clone(), parent.clone());
}
pub fn add_type_property(
&mut self,
id: &NodeId,
typ: &NodeId,
path: &[&QualifiedName],
node_class: NodeClass,
) {
let props = match self.type_properties.get_mut(typ) {
Some(x) => x,
None => self.type_properties.entry(typ.clone()).or_default(),
};
let path_owned: Vec<_> = path.iter().map(|n| (*n).to_owned()).collect();
props.insert(
TypePropertyKey {
path: path_owned.clone(),
},
TypeProperty {
node_class,
node_id: id.clone(),
},
);
self.property_to_type.insert(
id.clone(),
TypePropertyInverseRef {
type_id: typ.clone(),
path: path_owned,
},
);
}
pub fn remove(&mut self, node_id: &NodeId) -> bool {
if self.nodes.remove(node_id).is_some() {
let props = self.type_properties.remove(node_id);
if let Some(props) = props {
for prop in props.values() {
self.property_to_type.remove(&prop.node_id);
}
}
if let Some(parent) = self.subtypes_by_target.remove(node_id) {
if let Some(types) = self.subtypes_by_source.get_mut(&parent) {
types.remove(node_id);
}
}
return true;
}
if let Some(prop) = self.property_to_type.remove(node_id) {
let props = self.type_properties.get_mut(&prop.type_id);
if let Some(props) = props {
props.remove(&prop.path as &[QualifiedName]);
}
return true;
}
false
}
pub fn namespaces_mut(&mut self) -> &mut NamespaceMap {
&mut self.namespaces
}
pub fn namespaces(&self) -> &NamespaceMap {
&self.namespaces
}
pub fn get_all_children<'a>(&'a self, root: &'a NodeId) -> Vec<&'a NodeId> {
let mut res = Vec::new();
let mut roots = vec![root];
while let Some(root) = roots.pop() {
res.push(root);
let Some(children) = self.subtypes_by_source.get(root) else {
continue;
};
for child in children.iter() {
roots.push(child);
}
}
res
}
}