public void ApplyModellingRules(
ILocalNode instance,
ILocalNode typeDefinition,
ILocalNode templateDeclaration,
ushort namespaceIndex)
{
if (instance == null) throw new ArgumentNullException("instance");
if (typeDefinition == null) throw new ArgumentNullException("typeDefinition");
// check existing type definition.
UpdateTypeDefinition(instance, typeDefinition.NodeId);
// create list of declarations for the type definition (recursively collects definitions from supertypes).
List<DeclarationNode> declarations = new List<DeclarationNode>();
BuildDeclarationList(typeDefinition, declarations);
// add instance declaration if provided.
if (templateDeclaration != null)
{
DeclarationNode declaration = new DeclarationNode();
declaration.Node = templateDeclaration;
declaration.BrowsePath = String.Empty;
declarations.Add(declaration);
BuildDeclarationList(templateDeclaration, declarations);
}
// build list of instances to create.
List<ILocalNode> typeDefinitions = new List<ILocalNode>();
SortedDictionary<string,ILocalNode> instanceDeclarations = new SortedDictionary<string,ILocalNode>();
SortedDictionary<NodeId,ILocalNode> possibleTargets = new SortedDictionary<NodeId,ILocalNode>();
// create instances from declarations.
// subtypes appear in list last so traversing the list backwards find the overridden nodes first.
for (int ii = declarations.Count-1; ii >= 0; ii--)
{
DeclarationNode declaration = declarations[ii];
// update type definition list.
if (String.IsNullOrEmpty(declaration.BrowsePath))
{
typeDefinitions.Add(declaration.Node);
continue;
}
// skip declaration if instance already exists.
// (i.e. the declaration was overridden).
if (instanceDeclarations.ContainsKey(declaration.BrowsePath))
{
continue;
}
// update instance declaration list.
instanceDeclarations[declaration.BrowsePath] = declaration.Node;
// save the node as a possible target of references.
possibleTargets[declaration.Node.NodeId] = declaration.Node;
}
// build list of instances that already exist.
SortedDictionary<string,ILocalNode> existingInstances = new SortedDictionary<string,ILocalNode>();
BuildInstanceList(instance, String.Empty, existingInstances);
// maps the instance declaration onto an instance node.
Dictionary<NodeId,ILocalNode> instancesToCreate = new Dictionary<NodeId,ILocalNode>();
// apply modelling rules to instance declarations.
foreach (KeyValuePair<string,ILocalNode> current in instanceDeclarations)
{
string browsePath = current.Key;
ILocalNode instanceDeclaration = current.Value;
// check if the same instance has multiple browse paths to it.
ILocalNode newInstance = null;
if (instancesToCreate.TryGetValue(instanceDeclaration.NodeId, out newInstance))
{
continue;
}
// check for an existing instance.
if (existingInstances.TryGetValue(browsePath, out newInstance))
{
continue;
}
// apply modelling rule to determine whether to create a new instance.
NodeId modellingRule = instanceDeclaration.ModellingRule;
// always create a new instance if one does not already exist.
if (modellingRule == Objects.ModellingRule_Mandatory)
{
if (newInstance == null)
{
newInstance = instanceDeclaration.CreateCopy(CreateUniqueNodeId());
AddNode(newInstance);
}
}
// ignore optional instances unless one has been specified in the existing tree.
else if (modellingRule == Objects.ModellingRule_Optional)
{
if (newInstance == null)
{
continue;
}
}
// always use the declaration node.
else if (modellingRule == Objects.ModellingRule_MandatoryShared)
{
newInstance = instanceDeclaration;
}
// ignore any unknown modelling rules.
else
{
continue;
}
// save the mapping between the instance declaration and the new instance.
instancesToCreate[instanceDeclaration.NodeId] = newInstance;
}
// add references from type definitions to top level.
foreach (ILocalNode type in typeDefinitions)
{
foreach (IReference reference in type.References)
{
// ignore external references from type.
if (reference.TargetId.IsAbsolute)
{
continue;
}
// ignore subtype references.
if (m_nodes.TypeTree.IsTypeOf(reference.ReferenceTypeId, ReferenceTypeIds.HasSubtype))
{
continue;
}
// ignore targets that are not in the instance tree.
ILocalNode target = null;
if (!instancesToCreate.TryGetValue((NodeId)reference.TargetId, out target))
{
continue;
}
// add forward and backward reference.
AddReference(instance, reference.ReferenceTypeId, reference.IsInverse, target, true);
}
}
// add references between instance declarations.
foreach (ILocalNode instanceDeclaration in instanceDeclarations.Values)
{
// find the source for the references.
ILocalNode source = null;
if (!instancesToCreate.TryGetValue(instanceDeclaration.NodeId, out source))
{
continue;
}
// check if the source is a shared node.
bool sharedNode = Object.ReferenceEquals(instanceDeclaration, source);
foreach (IReference reference in instanceDeclaration.References)
{
// add external reference.
if (reference.TargetId.IsAbsolute)
{
if (!sharedNode)
{
AddReference(source, reference.ReferenceTypeId, reference.IsInverse, reference.TargetId);
}
continue;
}
// check for modelling rule.
if (reference.ReferenceTypeId == ReferenceTypeIds.HasModellingRule)
{
if (!source.References.Exists(ReferenceTypeIds.HasModellingRule, false, reference.TargetId, false, null))
{
AddReference(source, reference.ReferenceTypeId, false, reference.TargetId);
}
continue;
}
// check for type definition.
if (reference.ReferenceTypeId == ReferenceTypeIds.HasTypeDefinition)
{
if (!sharedNode)
{
UpdateTypeDefinition(source, instanceDeclaration.TypeDefinitionId);
}
continue;
}
// add targets that are not in the instance tree.
ILocalNode target = null;
if (!instancesToCreate.TryGetValue((NodeId)reference.TargetId, out target))
{
// don't update shared nodes because the reference should already exist.
if (sharedNode)
{
continue;
}
// top level references to the type definition node were already added.
if (reference.TargetId == typeDefinition.NodeId)
{
continue;
}
// see if a reference is allowed.
if (!IsExternalReferenceAllowed(reference.ReferenceTypeId))
{
continue;
}
// add one way reference.
source.References.Add(reference.ReferenceTypeId, reference.IsInverse, reference.TargetId);
continue;
}
// add forward and backward reference.
AddReference(source, reference.ReferenceTypeId, reference.IsInverse, target, true);
}
}
}