web/play-java-forms/src/main/java/play/data/format/Formatters.java
@SuppressWarnings("unchecked")
  public <A extends Annotation, T> Formatters register(
      final Class<T> clazz, final AnnotationFormatter<A, T> formatter) {
    final Class<? extends Annotation> annotationType =
        (Class<? extends Annotation>)
            GenericTypeResolver.resolveTypeArguments(
                formatter.getClass(), AnnotationFormatter.class)[0];

    conversion.addConverter(
        new ConditionalGenericConverter() {
          public Set<GenericConverter.ConvertiblePair> getConvertibleTypes() {
            Set<GenericConverter.ConvertiblePair> types = new HashSet<>();
            types.add(new GenericConverter.ConvertiblePair(clazz, String.class));
            return types;
          }

          public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
            return (sourceType.getAnnotation(annotationType) != null);
          }

          public Object convert(
              Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
            final A a = (A) sourceType.getAnnotation(annotationType);
            Locale locale = LocaleContextHolder.getLocale();
            try {
              return formatter.print(a, (T) source, locale);
            } catch (Exception ex) {
              throw new ConversionFailedException(sourceType, targetType, source, ex);
            }
          }

          public String toString() {
            return "@"
                + annotationType.getName()
                + " "
                + clazz.getName()
                + " -> "
                + String.class.getName()
                + ": "
                + formatter;
          }
        });

    conversion.addConverter(
        new ConditionalGenericConverter() {
          public Set<GenericConverter.ConvertiblePair> getConvertibleTypes() {
            Set<GenericConverter.ConvertiblePair> types = new HashSet<>();
            types.add(new GenericConverter.ConvertiblePair(String.class, clazz));
            return types;
          }

          public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
            return (targetType.getAnnotation(annotationType) != null);
          }

          public Object convert(
              Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
            final A a = (A) targetType.getAnnotation(annotationType);
            Locale locale = LocaleContextHolder.getLocale();
            try {
              return formatter.parse(a, (String) source, locale);
            } catch (Exception ex) {
              throw new ConversionFailedException(sourceType, targetType, source, ex);
            }
          }

          public String toString() {
            return String.class.getName()
                + " -> @"
                + annotationType.getName()
                + " "
                + clazz.getName()
                + ": "
                + formatter;
          }
        });

    return this;
  }
@SuppressWarnings("unchecked")
  public <A extends Annotation, T> Formatters register(
      final Class<T> clazz, final AnnotationFormatter<A, T> formatter) {
    final Class<? extends Annotation> annotationType =
        (Class<? extends Annotation>)
            GenericTypeResolver.resolveTypeArguments(
                formatter.getClass(), AnnotationFormatter.class)[0];

    conversion.addConverter(
        new ConditionalGenericConverter() {
          public Set<GenericConverter.ConvertiblePair> getConvertibleTypes() {
            Set<GenericConverter.ConvertiblePair> types = new HashSet<>();
            types.add(new GenericConverter.ConvertiblePair(clazz, String.class));
            return types;
          }

          public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
            return (sourceType.getAnnotation(annotationType) != null);
          }

          public Object convert(
              Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
            final A a = (A) sourceType.getAnnotation(annotationType);
            Locale locale = LocaleContextHolder.getLocale();
            try {
              return formatter.print(a, (T) source, locale);
            } catch (Exception ex) {
              throw new ConversionFailedException(sourceType, targetType, source, ex);
            }
          }

          public String toString() {
            return "@"
                + annotationType.getName()
                + " "
                + clazz.getName()
                + " -> "
                + String.class.getName()
                + ": "
                + formatter;
          }
        });

    conversion.addConverter(
        new ConditionalGenericConverter() {
          public Set<GenericConverter.ConvertiblePair> getConvertibleTypes() {
            Set<GenericConverter.ConvertiblePair> types = new HashSet<>();
            types.add(new GenericConverter.ConvertiblePair(String.class, clazz));
            return types;
          }

          public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
            return (targetType.getAnnotation(annotationType) != null);
          }

          public Object convert(
              Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
            final A a = (A) targetType.getAnnotation(annotationType);
            Locale locale = LocaleContextHolder.getLocale();
            try {
              return formatter.parse(a, (String) source, locale);
            } catch (Exception ex) {
              throw new ConversionFailedException(sourceType, targetType, source, ex);
            }
          }

          public String toString() {
            return String.class.getName()
                + " -> @"
                + annotationType.getName()
                + " "
                + clazz.getName()
                + ": "
                + formatter;
          }
        });

    return this;
  }
Registers an annotation-based formatter.

@param clazz class handled by this formatter
@param formatter the formatter to register
@param <A> the annotation type
@param <T> the type that will be parsed or printed
@return the modified Formatters object.