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.server.events.conversions;
  
  import org.eclipse.milo.opcua.stack.core.BuiltinDataType;
  import org.jetbrains.annotations.NotNull;
  import org.jetbrains.annotations.Nullable;
  import org.testng.annotations.Test;
  
  import static org.testng.Assert.assertEquals;
  import static org.testng.Assert.assertNull;
  import static org.testng.Assert.fail;
  
  abstract class AbstractConversionTest<S> {
  
      @Test
      public void testNullConversion() {
          for (BuiltinDataType targetType : BuiltinDataType.values()) {
              assertNull(convert(null, targetType, true));
              assertNull(convert(null, targetType, false));
          }
      }
  
      @Test
      public void testAllConversions() {
          for (BuiltinDataType targetType : BuiltinDataType.values()) {
              Conversion[] conversions = getConversions(targetType);
              ConversionType conversionType = getConversionType(targetType);
              BuiltinDataType sourceType = BuiltinDataType.fromBackingClass(getSourceClass());
  
              if (conversionType != ConversionType.NONE) {
                  if (conversions == null || conversions.length == 0) {
                      fail("expected conversions for " + targetType);
                  }
  
                  System.out.println(String.format("%s -> %s [%s]", sourceType, targetType, conversionType));
  
                  for (Conversion conversion : conversions) {
                      assertEquals(targetType, conversion.targetType);
  
                      S fromValue = getSourceClass().cast(conversion.fromValue);
                      Object targetValue = conversion.targetValue;
  
                      Object convertedValue = convert(fromValue, targetType, conversionType == ConversionType.IMPLICIT);
  
                      System.out.println(String.format("\tfromValue=%s targetValue=%s", fromValue, targetValue));
  
                      assertEquals(convertedValue, targetValue);
                  }
              } else {
                  if (conversions.length != 0) {
                      fail(String.format(
                          "conversions defined for %s -> %s " +
                              "but no ConversionType is defined", sourceType, targetType));
                  }
              }
          }
      }
  
      @Test
      public void testExplicitConversionsCalledImplicitlyAreNull() {
          for (BuiltinDataType targetType : BuiltinDataType.values()) {
              Conversion[] conversions = getConversions(targetType);
  
              for (Conversion conversion : conversions) {
                  ConversionType conversionType = getConversionType(conversion.targetType);
  
                  if (conversionType == ConversionType.EXPLICIT) {
                      S fromValue = getSourceClass().cast(conversion.fromValue);
  
                      Object convertedValue = convert(fromValue, targetType, true);
  
                      assertNull(convertedValue);
                  }
              }
          }
      }
  
      @Test
      public void testImplicitConversionsCalledExplicitly() {
          for (BuiltinDataType targetType : BuiltinDataType.values()) {
              Conversion[] conversions = getConversions(targetType);
  
              for (Conversion conversion : conversions) {
                  ConversionType conversionType = getConversionType(conversion.targetType);
  
                  if (conversionType == ConversionType.IMPLICIT) {
                      S fromValue = getSourceClass().cast(conversion.fromValue);
  
                      Object convertedValue = convert(fromValue, targetType, false);
  
                      System.out.println(String.format(
                          "[%s] fromValue=%s targetType=%s targetValue=%s",
                          conversionType, fromValue, targetType, conversion.targetValue));
  
                      assertEquals(convertedValue, conversion.targetValue);
                  }
              }
          }
      }
  
      protected Conversion c(@NotNull S fromValue, @NotNull Object targetValue) {
          Conversion c = new Conversion();
          c.fromValue = fromValue;
          c.targetValue = targetValue;
          c.targetType = BuiltinDataType.fromBackingClass(targetValue.getClass());
          return c;
      }
  
      protected Conversion c(@NotNull S fromValue, @Nullable Object targetValue, @NotNull BuiltinDataType targetType) {
          Conversion c = new Conversion();
          c.fromValue = fromValue;
          c.targetValue = targetValue;
          c.targetType = targetType;
          return c;
      }
  
      protected abstract Class<S> getSourceClass();
  
      public abstract Conversion[] getConversions(BuiltinDataType targetType);
  
      public abstract ConversionType getConversionType(BuiltinDataType targetType);
  
      /*
      {
          //@formatter:off
          switch (targetType) {
              case Boolean:   return ConversionType.IMPLICIT;
              case Byte:      return ConversionType.IMPLICIT;
              case Float:     return ConversionType.IMPLICIT;
              case Double:    return ConversionType.IMPLICIT;
              case Int16:     return ConversionType.IMPLICIT;
              case Int32:     return ConversionType.IMPLICIT;
              case Int64:     return ConversionType.IMPLICIT;
              case SByte:     return ConversionType.IMPLICIT;
              case String:    return ConversionType.EXPLICIT;
              case UInt16:    return ConversionType.IMPLICIT;
              case UInt32:    return ConversionType.IMPLICIT;
              case UInt64:    return ConversionType.IMPLICIT;
              default:        return ConversionType.NONE;
          }
          //@formatter:on
      }
      */
  
      // protected abstract Conversion[] getExplicitConversions();
  
      // protected abstract Conversion[] getImplicitConversions();
  
      protected abstract Object convert(Object fromValue, BuiltinDataType targetType, boolean implicit);
  
      static class Conversion {
          Object fromValue;
          Object targetValue;
          BuiltinDataType targetType;
      }
  
      enum ConversionType {
          EXPLICIT,
          IMPLICIT,
          NONE
      }
  
  }
  