/* * Copyright 2015 Kevin Herron * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.digitalpetri.opcua.stack.core.types.builtin; import java.util.Optional; import java.util.UUID; import javax.xml.bind.DatatypeConverter; import com.digitalpetri.opcua.stack.core.StatusCodes; import com.digitalpetri.opcua.stack.core.UaRuntimeException; import com.digitalpetri.opcua.stack.core.types.builtin.unsigned.UInteger; import com.digitalpetri.opcua.stack.core.types.builtin.unsigned.UShort; import com.digitalpetri.opcua.stack.core.types.builtin.unsigned.Unsigned; import com.digitalpetri.opcua.stack.core.types.enumerated.IdType; import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; import com.google.common.base.Objects; import com.google.common.primitives.UnsignedInteger; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; public final class ExpandedNodeId { public static final ExpandedNodeId NULL_VALUE = new ExpandedNodeId(NodeId.NULL_VALUE, null, 0); private final NodeId nodeId; private final String namespaceUri; private final long serverIndex; public ExpandedNodeId(NodeId nodeId) { this(nodeId, null, 0); } public ExpandedNodeId(NodeId nodeId, String namespaceUri, long serverIndex) { this.nodeId = nodeId; this.namespaceUri = namespaceUri; this.serverIndex = serverIndex; } /** * @param namespaceIndex the index for a namespace URI. An index of 0 is used for OPC UA defined NodeIds. * @param identifier the identifier for a node in the address space of an OPC UA Server. */ public ExpandedNodeId(UShort namespaceIndex, UInteger identifier, String namespaceUri, long serverIndex) { checkArgument(identifier.longValue() >= 0 && identifier.longValue() <= UnsignedInteger.MAX_VALUE.longValue()); this.nodeId = new NodeId(namespaceIndex, identifier); this.namespaceUri = namespaceUri; this.serverIndex = serverIndex; } /** * @param namespaceIndex the index for a namespace URI. An index of 0 is used for OPC UA defined NodeIds. * @param identifier the identifier for a node in the address space of an OPC UA Server. */ public ExpandedNodeId(int namespaceIndex, int identifier, String namespaceUri, long serverIndex) { this(Unsigned.ushort(namespaceIndex), Unsigned.uint(identifier), namespaceUri, serverIndex); } /** * @param namespaceIndex the index for a namespace URI. An index of 0 is used for OPC UA defined NodeIds. * @param identifier the identifier for a node in the address space of an OPC UA Server. */ public ExpandedNodeId(UShort namespaceIndex, String identifier, String namespaceUri, long serverIndex) { checkNotNull(identifier); this.nodeId = new NodeId(namespaceIndex, identifier); this.namespaceUri = namespaceUri; this.serverIndex = serverIndex; } /** * @param namespaceIndex the index for a namespace URI. An index of 0 is used for OPC UA defined NodeIds. * @param identifier the identifier for a node in the address space of an OPC UA Server. */ public ExpandedNodeId(int namespaceIndex, String identifier, String namespaceUri, long serverIndex) { this(Unsigned.ushort(namespaceIndex), identifier, namespaceUri, serverIndex); } /** * @param namespaceIndex the index for a namespace URI. An index of 0 is used for OPC UA defined NodeIds. * @param identifier the identifier for a node in the address space of an OPC UA Server. */ public ExpandedNodeId(UShort namespaceIndex, UUID identifier, String namespaceUri, long serverIndex) { checkNotNull(identifier); this.nodeId = new NodeId(namespaceIndex, identifier); this.namespaceUri = namespaceUri; this.serverIndex = serverIndex; } /** * @param namespaceIndex the index for a namespace URI. An index of 0 is used for OPC UA defined NodeIds. * @param identifier the identifier for a node in the address space of an OPC UA Server. */ public ExpandedNodeId(int namespaceIndex, UUID identifier, String namespaceUri, long serverIndex) { this(Unsigned.ushort(namespaceIndex), identifier, namespaceUri, serverIndex); } /** * @param namespaceIndex the index for a namespace URI. An index of 0 is used for OPC UA defined NodeIds. * @param identifier the identifier for a node in the address space of an OPC UA Server. */ public ExpandedNodeId(UShort namespaceIndex, ByteString identifier, String namespaceUri, long serverIndex) { checkNotNull(identifier); this.nodeId = new NodeId(namespaceIndex, identifier); this.namespaceUri = namespaceUri; this.serverIndex = serverIndex; } /** * @param namespaceIndex the index for a namespace URI. An index of 0 is used for OPC UA defined NodeIds. * @param identifier the identifier for a node in the address space of an OPC UA Server. */ public ExpandedNodeId(int namespaceIndex, ByteString identifier, String namespaceUri, long serverIndex) { this(Unsigned.ushort(namespaceIndex), identifier, namespaceUri, serverIndex); } public UShort getNamespaceIndex() { return nodeId.getNamespaceIndex(); } public Object getIdentifier() { return nodeId.getIdentifier(); } public IdType getType() { return nodeId.getType(); } public String getNamespaceUri() { return namespaceUri; } public long getServerIndex() { return serverIndex; } public boolean isLocal() { return serverIndex == 0; } public boolean isNull() { return nodeId.isNull(); } public boolean isNotNull() { return !isNull(); } /** * If this {@link ExpandedNodeId} resides on the local server ({@code serverIndex == 0}), return its representation * as a local {@link NodeId}. * * @return a local {@link NodeId}, if {@code serverIndex == 0}. */ public Optional<NodeId> local() { return isLocal() ? Optional.of(nodeId) : Optional.empty(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ExpandedNodeId that = (ExpandedNodeId) o; return Objects.equal(serverIndex, that.serverIndex) && Objects.equal(nodeId.getIdentifier(), that.nodeId.getIdentifier()) && (Objects.equal(namespaceUri, that.namespaceUri) || Objects.equal(nodeId.getNamespaceIndex(), that.nodeId.getNamespaceIndex())); } @Override public int hashCode() { int result = nodeId != null ? nodeId.hashCode() : 0; result = 31 * result + (namespaceUri != null ? namespaceUri.hashCode() : 0); result = 31 * result + (int) (serverIndex ^ (serverIndex >>> 32)); return result; } @Override public String toString() { ToStringHelper helper = MoreObjects.toStringHelper(this); if (namespaceUri != null && namespaceUri.length() > 0) { helper.add("ns", namespaceUri); } else { helper.add("ns", getNamespaceIndex()); } helper.add("id", getIdentifier()); helper.add("serverIndex", getServerIndex()); return helper.toString(); } public String toParseableString() { StringBuilder sb = new StringBuilder(); sb.append("svr=").append(serverIndex).append(";"); if (namespaceUri != null) { sb.append("nsu=").append(namespaceUri).append(";"); } else { int namespaceIndex = getNamespaceIndex().intValue(); if (namespaceIndex > 0) { sb.append("ns=").append(getNamespaceIndex().intValue()).append(";"); } } switch (getType()) { case Numeric: sb.append("i=").append(getIdentifier()); break; case String: sb.append("s=").append(getIdentifier()); break; case Guid: sb.append("g=").append(getIdentifier()); break; case Opaque: ByteString bs = (ByteString) getIdentifier(); if (bs.isNull()) sb.append("b="); else sb.append("b=").append(DatatypeConverter.printBase64Binary(bs.bytes())); break; } return sb.toString(); } public static ExpandedNodeId parse(String s) { try { String[] parts = s.split(";"); NodeId nodeId = NodeId.parse(parts[parts.length - 1]); int serverIndex = 0; int namespaceIndex = 0; String namespaceUri = null; Object identifier = nodeId.getIdentifier(); for (String part : parts) { String[] ss = part.split("="); if ("svr".equals(ss[0])) { serverIndex = Integer.parseInt(ss[1]); } else if ("ns".equals(ss[0])) { namespaceIndex = Integer.parseInt(ss[1]); } else if ("nsu".equals(ss[0])) { namespaceUri = ss[1]; } } switch (nodeId.getType()) { case Guid: return new ExpandedNodeId(namespaceIndex, (UUID) identifier, namespaceUri, serverIndex); case Numeric: return new ExpandedNodeId(namespaceIndex, ((UInteger) identifier).intValue(), namespaceUri, serverIndex); case Opaque: return new ExpandedNodeId(namespaceIndex, (ByteString) identifier, namespaceUri, serverIndex); case String: return new ExpandedNodeId(namespaceIndex, (String) identifier, namespaceUri, serverIndex); default: throw new IllegalStateException("IdType " + nodeId.getType()); } } catch (Throwable t) { throw new UaRuntimeException(StatusCodes.Bad_NodeIdInvalid, t); } } }