code: /*
   * Copyright (c) 2019 the Eclipse Milo Authors
   *
   * This program and the accompanying materials are made
   * available under the terms of the Eclipse Public License 2.0
   * which is available at https://www.eclipse.org/legal/epl-2.0/
   *
   * SPDX-License-Identifier: EPL-2.0
   */
  
  package org.eclipse.milo.opcua.sdk.core;
  
  import java.util.Objects;
  
  import org.eclipse.milo.opcua.sdk.client.DataTypeTreeBuilder;
  import org.eclipse.milo.opcua.sdk.test.AbstractClientServerTest;
  import org.eclipse.milo.opcua.stack.core.BuiltinDataType;
  import org.eclipse.milo.opcua.stack.core.Identifiers;
  import org.eclipse.milo.opcua.stack.core.UaException;
  import org.eclipse.milo.opcua.stack.core.types.builtin.ByteString;
  import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime;
  import org.eclipse.milo.opcua.stack.core.types.builtin.ExtensionObject;
  import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
  import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UByte;
  import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
  import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.ULong;
  import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort;
  import org.eclipse.milo.opcua.stack.core.util.Tree;
  import org.junit.jupiter.api.BeforeAll;
  import org.junit.jupiter.api.Test;
  import org.junit.jupiter.api.TestInstance;
  
  import static org.junit.jupiter.api.Assertions.assertEquals;
  import static org.junit.jupiter.api.Assertions.assertFalse;
  import static org.junit.jupiter.api.Assertions.assertNotNull;
  import static org.junit.jupiter.api.Assertions.assertTrue;
  
  @TestInstance(TestInstance.Lifecycle.PER_CLASS)
  public class DataTypeTreeTest extends AbstractClientServerTest {
  
      private DataTypeTree dataTypeTree;
  
      @BeforeAll
      public void buildDataTypeTree() throws UaException {
          dataTypeTree = DataTypeTreeBuilder.build(client);
      }
  
      @Test
      public void testGetTree() {
          dataTypeTree.getTree().traverseWithDepth((dataType, depth) -> {
              for (int i = 0; i < depth; i++) {
                  System.out.print("\t");
              }
              System.out.println(dataType.getBrowseName().toParseableString());
          });
      }
  
      @Test
      public void testGetBackingClass() {
          // all subtypes of String are backed by String.class
          checkSubtypes(Identifiers.String, String.class);
  
          // all subtypes of DateTime are backed by DateTime.class
          checkSubtypes(Identifiers.DateTime, DateTime.class);
  
          // all subtypes of ByteString are backed by ByteString.class
          checkSubtypes(Identifiers.ByteString, ByteString.class);
  
          // all subtypes of NodeId are backed by NodeId.class
          checkSubtypes(Identifiers.NodeId, NodeId.class);
  
          // all subtypes of Structure are backed by ExtensionObject.class
          checkSubtypes(Identifiers.Structure, ExtensionObject.class);
  
          // all subtypes of Double are backed by Double.class
          checkSubtypes(Identifiers.Double, Double.class);
  
          // all subtypes of UInt32 are backed by UInteger.class
          checkSubtypes(Identifiers.UInt32, UInteger.class);
  
          // all subtypes of UInt64 are backed by ULong.class
          checkSubtypes(Identifiers.UInt64, ULong.class);
  
          // all subtypes of Enumeration are backed by Integer.class
          checkSubtypes(Identifiers.Enumeration, Integer.class);
      }
  
      private void checkSubtypes(NodeId dataTypeId, Class<?> expectedBackingClass) {
          Tree<DataTypeTree.DataType> nodeIdNode = dataTypeTree.getTreeNode(dataTypeId);
          assertNotNull(nodeIdNode);
          nodeIdNode.traverse(dataType -> {
              Class<?> backingClass = dataTypeTree.getBackingClass(dataType.getNodeId());
              System.out.println(dataType.getBrowseName().toParseableString() + " : " + backingClass);
              assertEquals(expectedBackingClass, backingClass);
          });
      }
  
      @Test
      public void testGetBuiltinType() {
          // Check all the builtin types
          for (BuiltinDataType expectedType : BuiltinDataType.values()) {
              BuiltinDataType builtinType = dataTypeTree.getBuiltinType(expectedType.getNodeId());
  
              assertEquals(expectedType, builtinType);
          }
  
          // Check that subtypes resolve to their builtin types
          assertEquals(BuiltinDataType.String, dataTypeTree.getBuiltinType(Identifiers.NumericRange));
          assertEquals(BuiltinDataType.DateTime, dataTypeTree.getBuiltinType(Identifiers.Date));
          assertEquals(BuiltinDataType.ByteString, dataTypeTree.getBuiltinType(Identifiers.Image));
          assertEquals(BuiltinDataType.ByteString, dataTypeTree.getBuiltinType(Identifiers.ImageBMP));
          assertEquals(BuiltinDataType.NodeId, dataTypeTree.getBuiltinType(Identifiers.SessionAuthenticationToken));
          assertEquals(BuiltinDataType.ExtensionObject, dataTypeTree.getBuiltinType(Identifiers.TrustListDataType));
          assertEquals(BuiltinDataType.Double, dataTypeTree.getBuiltinType(Identifiers.Duration));
          assertEquals(BuiltinDataType.UInt32, dataTypeTree.getBuiltinType(Identifiers.IntegerId));
          assertEquals(BuiltinDataType.UInt64, dataTypeTree.getBuiltinType(Identifiers.BitFieldMaskDataType));
          // note: enumerations resolve to BaseDataType aka Variant
          assertEquals(BuiltinDataType.Variant, dataTypeTree.getBuiltinType(Identifiers.NamingRuleType));
      }
  
      @Test
      public void testIsAssignable() {
          assertTrue(dataTypeTree.isAssignable(Identifiers.NumericRange, String.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Date, DateTime.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Image, ByteString.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.ImageBMP, ByteString.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.SessionAuthenticationToken, NodeId.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.TrustListDataType, ExtensionObject.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Number, Number.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Number, Float.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Number, Double.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Number, Byte.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Number, Short.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Number, Integer.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Number, Long.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Number, UByte.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Number, UShort.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Number, UInteger.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Number, ULong.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Integer, Byte.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Integer, Short.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Integer, Integer.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Integer, Long.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.UInteger, UByte.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.UInteger, UShort.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.UInteger, UInteger.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.UInteger, ULong.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.Duration, Double.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.BitFieldMaskDataType, ULong.class));
          assertTrue(dataTypeTree.isAssignable(Identifiers.NamingRuleType, Integer.class));
  
          assertFalse(dataTypeTree.isAssignable(Identifiers.UInteger, Byte.class));
          assertFalse(dataTypeTree.isAssignable(Identifiers.UInteger, Short.class));
          assertFalse(dataTypeTree.isAssignable(Identifiers.UInteger, Integer.class));
          assertFalse(dataTypeTree.isAssignable(Identifiers.UInteger, Long.class));
          assertFalse(dataTypeTree.isAssignable(Identifiers.Integer, UByte.class));
          assertFalse(dataTypeTree.isAssignable(Identifiers.Integer, UShort.class));
          assertFalse(dataTypeTree.isAssignable(Identifiers.Integer, UInteger.class));
          assertFalse(dataTypeTree.isAssignable(Identifiers.Integer, ULong.class));
          assertFalse(dataTypeTree.isAssignable(Identifiers.Duration, Float.class));
      }
  
      @Test
      public void testGetEncodingIds() {
          Tree<DataTypeTree.DataType> treeNode = dataTypeTree.getTreeNode(Identifiers.Structure);
          assertNotNull(treeNode);
  
          treeNode.traverse(dataType -> {
              if (!Objects.equals(dataType.getBrowseName().getName(), "Structure")) {
                  assertNotNull(dataType.getBinaryEncodingId());
                  assertNotNull(dataType.getXmlEncodingId());
                  assertNotNull(dataTypeTree.getBinaryEncodingId(dataType.getNodeId()));
                  assertNotNull(dataTypeTree.getXmlEncodingId(dataType.getNodeId()));
              }
          });
      }
  
  }
  