Skip to content

Commit

Permalink
Better NodeId/ExpandedNodeId built in types handling (#2230)
Browse files Browse the repository at this point in the history
- A NodeId string can be assigned to a NodeId and is automatically parsed. However, if the string contains a "nsu=" it is considered a string type NodeId instead of decoding the namespaceUri. To avoid this error in the code an `ArgumentException` is thrown to uncover these coding errors. Such a variable needs to be an ExpandedNodeId to be decoded correctly.
- Breaking change: To reduce coding errors the user group decided to throw also on a constructor call which does not contain a nodeid type prefix and namespace index. e.g. `new NodeId("Testnode")`. If somebody wants a string NodeId in Namespace 0 then call the constructor `new NodeId("TestNode", 0)`
- Implement IEquatable<NodeId>/IEquatable<ExpandedNodeId>
- Tests to ensure .Distinct treats all Null NodeIds as identical.
- Unit tests for constructor behavior.
  • Loading branch information
mregen committed Jul 28, 2023
1 parent 04cd9bc commit 1f17aad
Show file tree
Hide file tree
Showing 13 changed files with 1,791 additions and 1,239 deletions.
116 changes: 73 additions & 43 deletions Stack/Opc.Ua.Core/Types/BuiltIn/ExpandedNodeId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace Opc.Ua
/// Extends a node id by adding a complete namespace URI.
/// </remarks>
[DataContract(Namespace = Namespaces.OpcUaXsd)]
public class ExpandedNodeId : ICloneable, IComparable, IFormattable
public class ExpandedNodeId : ICloneable, IComparable, IEquatable<ExpandedNodeId>, IFormattable
{
#region Constructors
/// <summary>
Expand Down Expand Up @@ -315,7 +315,7 @@ public ExpandedNodeId(byte[] value, string namespaceUri)
public ExpandedNodeId(string text)
{
Initialize();
m_nodeId = new NodeId(text);
InternalParse(text);
}

/// <summary>
Expand Down Expand Up @@ -654,47 +654,7 @@ public static ExpandedNodeId Parse(string text)
return ExpandedNodeId.Null;
}

uint serverIndex = 0;

// parse the server index if present.
if (text.StartsWith("svr=", StringComparison.Ordinal))
{
int index = text.IndexOf(';');

if (index == -1)
{
throw new ServiceResultException(StatusCodes.BadNodeIdInvalid, "Invalid server index.");
}

serverIndex = Convert.ToUInt32(text.Substring(4, index - 4), CultureInfo.InvariantCulture);

text = text.Substring(index + 1);
}

string namespaceUri = null;

// parse the namespace uri if present.
if (text.StartsWith("nsu=", StringComparison.Ordinal))
{
int index = text.IndexOf(';');

if (index == -1)
{
throw new ServiceResultException(StatusCodes.BadNodeIdInvalid, "Invalid namespace uri.");
}

StringBuilder buffer = new StringBuilder();

UnescapeUri(text, 4, index, buffer);
namespaceUri = buffer.ToString();
text = text.Substring(index + 1);
}

// parse the node id.
NodeId nodeId = NodeId.Parse(text);

// craete the node id.
return new ExpandedNodeId(nodeId, namespaceUri, serverIndex);
return new ExpandedNodeId(text);
}
catch (Exception e)
{
Expand Down Expand Up @@ -949,6 +909,15 @@ public override int GetHashCode()

return (value1.CompareTo(value2) != 0);
}

/// <summary>
/// Implements <see cref="IEquatable{T}"/>.Equals(T)"/>
/// </summary>
/// <param name="other">The other ExpandedNodeId.</param>
public bool Equals(ExpandedNodeId other)
{
return (CompareTo(other) == 0);
}
#endregion

#region IFormattable Members
Expand Down Expand Up @@ -1189,6 +1158,67 @@ public static NodeId Parse(string text, NamespaceTable namespaceUris)
private static readonly ExpandedNodeId s_Null = new ExpandedNodeId();
#endregion

#region Private Methods
/// <summary>
/// Parses a expanded node id string and sets the properties.
/// </summary>
/// <param name="text">The ExpandedNodeId value as a string.</param>
private void InternalParse(string text)
{
uint serverIndex = 0;
string namespaceUri = null;
try
{
// parse the server index if present.
if (text.StartsWith("svr=", StringComparison.Ordinal))
{
int index = text.IndexOf(';');

if (index == -1)
{
throw new ServiceResultException(StatusCodes.BadNodeIdInvalid, "Invalid server index.");
}

serverIndex = Convert.ToUInt32(text.Substring(4, index - 4), CultureInfo.InvariantCulture);

text = text.Substring(index + 1);
}

// parse the namespace uri if present.
if (text.StartsWith("nsu=", StringComparison.Ordinal))
{
int index = text.IndexOf(';');

if (index == -1)
{
throw new ServiceResultException(StatusCodes.BadNodeIdInvalid, "Invalid namespace uri.");
}

StringBuilder buffer = new StringBuilder();

UnescapeUri(text, 4, index, buffer);
namespaceUri = buffer.ToString();
text = text.Substring(index + 1);
}
}
catch (Exception e)
{
throw new ServiceResultException(
StatusCodes.BadNodeIdInvalid,
Utils.Format("Cannot parse expanded node id text: '{0}'", text),
e);
}

// parse the node id.
NodeId nodeId = NodeId.InternalParse(text, serverIndex != 0 || !string.IsNullOrEmpty(namespaceUri));

// set the properties.
m_nodeId = nodeId;
m_namespaceUri = namespaceUri;
m_serverIndex = serverIndex;
}
#endregion

#region Private Fields
private NodeId m_nodeId;
private string m_namespaceUri;
Expand Down

0 comments on commit 1f17aad

Please sign in to comment.