/**
 * Converts dates and times to an R representation.
 *
 * <p>The following are represented as strings:
 *
 * <ul>
 *   <li>LocalDate
 * </ul>
 *
 * The following are represented as doubles (seconds since 1970-01-01 epoch):
 *
 * <ul>
 *   <li>Expiry*
 *   <li>Instant
 *   <li>ZonedDateTime*
 * </ul>
 *
 * * although sent to R as a double, they can also be received as strings
 */
public class DateTimeConverter extends AbstractTypeConverter {

  private static final JavaTypeInfo<Value> VALUE = JavaTypeInfo.builder(Value.class).get();
  private static final JavaTypeInfo<Value> VALUE_ALLOW_NULL =
      JavaTypeInfo.builder(Value.class).allowNull().get();
  private static final JavaTypeInfo<Instant> INSTANT = JavaTypeInfo.builder(Instant.class).get();
  private static final JavaTypeInfo<Instant> INSTANT_ALLOW_NULL =
      JavaTypeInfo.builder(Instant.class).allowNull().get();
  private static final JavaTypeInfo<LocalDate> LOCAL_DATE =
      JavaTypeInfo.builder(LocalDate.class).get();
  private static final JavaTypeInfo<LocalDate> LOCAL_DATE_ALLOW_NULL =
      JavaTypeInfo.builder(LocalDate.class).allowNull().get();
  private static final JavaTypeInfo<ZonedDateTime> ZONED_DATE_TIME =
      JavaTypeInfo.builder(ZonedDateTime.class).get();
  private static final JavaTypeInfo<ZonedDateTime> ZONED_DATE_TIME_ALLOW_NULL =
      JavaTypeInfo.builder(ZonedDateTime.class).allowNull().get();
  private static final JavaTypeInfo<Expiry> EXPIRY = JavaTypeInfo.builder(Expiry.class).get();
  private static final JavaTypeInfo<Expiry> EXPIRY_ALLOW_NULL =
      JavaTypeInfo.builder(Expiry.class).allowNull().get();

  private static final TypeMap TO_VALUE =
      TypeMap.of(ZERO_LOSS, INSTANT, LOCAL_DATE)
          .with(MINOR_LOSS, ZONED_DATE_TIME)
          .with(MAJOR_LOSS, EXPIRY);
  private static final TypeMap TO_VALUE_ALLOW_NULL =
      TypeMap.of(ZERO_LOSS, INSTANT_ALLOW_NULL, LOCAL_DATE_ALLOW_NULL)
          .with(MINOR_LOSS, ZONED_DATE_TIME_ALLOW_NULL)
          .with(MAJOR_LOSS, EXPIRY_ALLOW_NULL);
  private static final TypeMap FROM_VALUE = TypeMap.of(MINOR_LOSS, VALUE);
  private static final TypeMap FROM_VALUE_ALLOW_NULL = TypeMap.of(MINOR_LOSS, VALUE_ALLOW_NULL);

  @Override
  public boolean canConvertTo(final JavaTypeInfo<?> targetType) {
    final Class<?> clazz = targetType.getRawClass();
    if (clazz == Value.class) {
      return true;
    } else {
      for (JavaTypeInfo<?> toData :
          (targetType.isAllowNull() ? TO_VALUE_ALLOW_NULL : TO_VALUE).keySet()) {
        if (clazz == toData.getRawClass()) {
          return true;
        }
      }
      return false;
    }
  }

  @Override
  public void convertValue(
      final ValueConversionContext conversionContext,
      final Object value,
      final JavaTypeInfo<?> type) {
    if ((value == null) && type.isAllowNull()) {
      conversionContext.setResult(null);
      return;
    }
    final Class<?> clazz = type.getRawClass();
    if (clazz == Value.class) {
      if (value instanceof LocalDate) {
        conversionContext.setResult(ValueUtils.of(((LocalDate) value).toString()));
      } else if (value instanceof InstantProvider) {
        conversionContext.setResult(
            ValueUtils.of((double) ((InstantProvider) value).toInstant().getEpochSeconds()));
      } else {
        conversionContext.setFail();
      }
    } else if (clazz == LocalDate.class) {
      final String str = ((Value) value).getStringValue();
      if (str != null) {
        try {
          conversionContext.setResult(LocalDate.parse(str));
        } catch (CalendricalException e) {
          conversionContext.setFail();
        }
      } else {
        conversionContext.setFail();
      }
    } else if (clazz == Instant.class) {
      final Double epochSeconds = ((Value) value).getDoubleValue();
      if (epochSeconds != null) {
        conversionContext.setResult(Instant.ofEpochSeconds(epochSeconds.longValue()));
      } else {
        conversionContext.setFail();
      }
    } else if (clazz == ZonedDateTime.class) {
      final ZonedDateTime zdt = valueToZonedDateTime((Value) value);
      if (zdt != null) {
        conversionContext.setResult(zdt);
      } else {
        conversionContext.setFail();
      }
    } else if (clazz == Expiry.class) {
      final ZonedDateTime zdt = valueToZonedDateTime((Value) value);
      if (zdt != null) {
        conversionContext.setResult(new Expiry(zdt));
      } else {
        conversionContext.setFail();
      }
    } else {
      conversionContext.setFail();
    }
  }

  private static ZonedDateTime valueToZonedDateTime(final Value v) {
    final Double epochSeconds = v.getDoubleValue();
    if (epochSeconds != null) {
      return ZonedDateTime.ofEpochSeconds(epochSeconds.longValue(), TimeZone.UTC);
    } else {
      final String dateTime = v.getStringValue();
      if (dateTime != null) {
        try {
          return ZonedDateTime.parse(dateTime);
        } catch (CalendricalException e) {
          // Ignore
        }
        try {
          return ZonedDateTime.of(LocalDate.parse(dateTime), LocalTime.MIDDAY, TimeZone.UTC);
        } catch (CalendricalException e) {
          // Ignore
        }
      }
    }
    return null;
  }

  @Override
  public Map<JavaTypeInfo<?>, Integer> getConversionsTo(final JavaTypeInfo<?> targetType) {
    final Class<?> clazz = targetType.getRawClass();
    if (clazz == Value.class) {
      return targetType.isAllowNull() ? TO_VALUE_ALLOW_NULL : TO_VALUE;
    } else {
      return targetType.isAllowNull() ? FROM_VALUE_ALLOW_NULL : FROM_VALUE;
    }
  }
}
Example #2
0
/**
 * Provides the most primitive level of view client type conversion. View clients are converted
 * to/from the {@link ViewClientKey} representation which is least efficient but all that is
 * available in the general case. A specific language binding that can track objects should replace
 * this with a conversion scheme that can work with the references detached from {@link
 * UserViewClients}.
 *
 * <p>It is expected that functions which take a {@link ViewClientHandle} as a parameter should
 * unlock it when they are done.
 */
public class UserViewClientConverter extends AbstractTypeConverter {

  /** Default instance. */
  public static final UserViewClientConverter INSTANCE = new UserViewClientConverter();

  private static final JavaTypeInfo<ViewClientKey> VIEW_CLIENT_KEY =
      JavaTypeInfo.builder(ViewClientKey.class).get();
  private static final JavaTypeInfo<ViewClientHandle> VIEW_CLIENT_HANDLE =
      JavaTypeInfo.builder(ViewClientHandle.class).get();

  private static final TypeMap TO_VIEW_CLIENT_KEY = TypeMap.of(ZERO_LOSS, VIEW_CLIENT_HANDLE);
  private static final TypeMap TO_VIEW_CLIENT_HANDLE = TypeMap.of(ZERO_LOSS, VIEW_CLIENT_KEY);

  protected UserViewClientConverter() {}

  /**
   * The key used by the default type converter. A binding specific converter should be declared
   * with this key to replace the default.
   *
   * @return the key
   */
  public static String getTypeConverterKeyImpl() {
    return UserViewClient.class.getSimpleName();
  }

  @Override
  public String getTypeConverterKey() {
    return getTypeConverterKeyImpl();
  }

  @Override
  public boolean canConvertTo(final JavaTypeInfo<?> targetType) {
    return (targetType.getRawClass() == ViewClientKey.class)
        || (targetType.getRawClass() == ViewClientHandle.class);
  }

  @Override
  public void convertValue(
      final ValueConversionContext conversionContext,
      final Object value,
      final JavaTypeInfo<?> type) {
    if (type.getRawClass() == ViewClientKey.class) {
      final ViewClientHandle viewClient = (ViewClientHandle) value;
      conversionContext.setResult(viewClient.get().getViewClientKey());
      viewClient.unlock();
    } else {
      final ViewClientKey key = (ViewClientKey) value;
      conversionContext.setResult(
          conversionContext.getUserContext().getViewClients().lockViewClient(key));
    }
  }

  @Override
  public Map<JavaTypeInfo<?>, Integer> getConversionsTo(final JavaTypeInfo<?> targetType) {
    if (targetType.getRawClass() == ViewClientKey.class) {
      return TO_VIEW_CLIENT_KEY;
    } else {
      return TO_VIEW_CLIENT_HANDLE;
    }
  }
}
/** Basic conversions to/from the {@link Value} type. */
public final class ValueConverter implements TypeConverter {

  private static final JavaTypeInfo<Value> VALUE_NOT_NULL = JavaTypeInfo.builder(Value.class).get();
  private static final JavaTypeInfo<Value> VALUE_NULL =
      JavaTypeInfo.builder(Value.class).allowNull().get();
  private static final JavaTypeInfo<Boolean> BOOLEAN_NOT_NULL =
      JavaTypeInfo.builder(Boolean.class).get();
  private static final JavaTypeInfo<Boolean> BOOLEAN_NULL =
      JavaTypeInfo.builder(Boolean.class).allowNull().get();
  private static final JavaTypeInfo<Integer> INTEGER_NOT_NULL =
      JavaTypeInfo.builder(Integer.class).get();
  private static final JavaTypeInfo<Integer> INTEGER_NULL =
      JavaTypeInfo.builder(Integer.class).allowNull().get();
  private static final JavaTypeInfo<Double> DOUBLE_NOT_NULL =
      JavaTypeInfo.builder(Double.class).get();
  private static final JavaTypeInfo<Double> DOUBLE_NULL =
      JavaTypeInfo.builder(Double.class).allowNull().get();
  private static final JavaTypeInfo<String> STRING_NOT_NULL =
      JavaTypeInfo.builder(String.class).get();
  private static final JavaTypeInfo<String> STRING_NULL =
      JavaTypeInfo.builder(String.class).allowNull().get();
  private static final JavaTypeInfo<FudgeMsg> MESSAGE_NOT_NULL =
      JavaTypeInfo.builder(FudgeMsg.class).get();
  private static final JavaTypeInfo<FudgeMsg> MESSAGE_NULL =
      JavaTypeInfo.builder(FudgeMsg.class).allowNull().get();

  private static final TypeMap TO_VALUE_NOT_NULL =
      TypeMap.of(
          ZERO_LOSS,
          BOOLEAN_NOT_NULL,
          INTEGER_NOT_NULL,
          DOUBLE_NOT_NULL,
          STRING_NOT_NULL,
          MESSAGE_NOT_NULL);
  private static final TypeMap TO_VALUE_ALLOW_NULL =
      TypeMap.of(ZERO_LOSS, BOOLEAN_NULL, INTEGER_NULL, DOUBLE_NULL, STRING_NULL, MESSAGE_NULL);
  private static final TypeMap FROM_VALUE_NOT_NULL = TypeMap.of(ZERO_LOSS, VALUE_NOT_NULL);
  private static final TypeMap FROM_VALUE_ALLOW_NULL = TypeMap.of(ZERO_LOSS, VALUE_NULL);

  @Override
  public boolean canConvertTo(JavaTypeInfo<?> targetType) {
    final Class<?> clazz = targetType.getRawClass();
    if (clazz == Value.class) {
      return true;
    } else {
      for (JavaTypeInfo<?> toData :
          (targetType.isAllowNull() ? TO_VALUE_ALLOW_NULL : TO_VALUE_NOT_NULL).keySet()) {
        if (clazz == toData.getRawClass()) {
          return true;
        }
      }
      return false;
    }
  }

  @Override
  public Map<JavaTypeInfo<?>, Integer> getConversionsTo(JavaTypeInfo<?> targetType) {
    final Class<?> clazz = targetType.getRawClass();
    if (clazz == Value.class) {
      return targetType.isAllowNull() ? TO_VALUE_ALLOW_NULL : TO_VALUE_NOT_NULL;
    } else {
      return targetType.isAllowNull() ? FROM_VALUE_ALLOW_NULL : FROM_VALUE_NOT_NULL;
    }
  }

  @Override
  public String toString() {
    return TypeConverter.class.getSimpleName() + "[to/from " + Value.class.getName() + "]";
  }

  @Override
  public void convertValue(
      ValueConversionContext conversionContext, Object valueObject, JavaTypeInfo<?> type) {
    final Class<?> clazz = type.getRawClass();
    if (clazz == Value.class) {
      if (valueObject instanceof Boolean) {
        conversionContext.setResult(ValueUtil.of((Boolean) valueObject));
      } else if (valueObject instanceof Integer) {
        conversionContext.setResult(ValueUtil.of((Integer) valueObject));
      } else if (valueObject instanceof Double) {
        conversionContext.setResult(ValueUtil.of((Double) valueObject));
      } else if (valueObject instanceof String) {
        conversionContext.setResult(ValueUtil.of((String) valueObject));
      } else if (valueObject instanceof FudgeMsg) {
        conversionContext.setResult(ValueUtil.of((FudgeMsg) valueObject));
      } else {
        conversionContext.setFail();
      }
      return;
    } else {
      if (valueObject instanceof Value) {
        final Value value = (Value) valueObject;
        if (ValueUtil.isNull(value)) {
          if (type.isAllowNull()) {
            conversionContext.setResult(null);
          } else {
            conversionContext.setFail();
          }
        } else if (clazz == Boolean.class) {
          if (value.getBoolValue() != null) {
            conversionContext.setResult(value.getBoolValue());
          } else {
            conversionContext.setFail();
          }
        } else if (clazz == Integer.class) {
          if (value.getIntValue() != null) {
            conversionContext.setResult(value.getIntValue());
          } else {
            conversionContext.setFail();
          }
        } else if (clazz == Double.class) {
          if (value.getDoubleValue() != null) {
            conversionContext.setResult(value.getDoubleValue());
          } else {
            conversionContext.setFail();
          }
        } else if (clazz == String.class) {
          if (value.getStringValue() != null) {
            conversionContext.setResult(value.getStringValue());
          } else {
            conversionContext.setFail();
          }
        } else if (clazz == FudgeMsg.class) {
          if (value.getMessageValue() != null) {
            conversionContext.setResult(value.getMessageValue());
          } else {
            conversionContext.setFail();
          }
        } else {
          conversionContext.setFail();
        }
        return;
      } else if (valueObject == null) {
        if (type.isAllowNull()) {
          conversionContext.setResult(null);
          return;
        }
      }
    }
    conversionContext.setFail();
  }
}