'Why method print() of my class CustomDateFormatter using with my custom spring annotation is never called?

I have a issue with creation of custom annotation in springboot application: I've got a form with few fields with datepicker. Dates must have to be parsed in 2 formats "dd/MM/yyyy" or "yyyy/MM/dd". To achieve that , as annotation @DateTimeFormat on a field of Entity only use one pattern, I decided to create my custom DateFormatter to be able to have my 2 formats:

public class CustomDateFormatter implements Formatter<Date> {

final String DATE_PATTERN_FR = "^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[012])/([0-9]{4})$";
final String DATE_PATTERN_EN = "^([0-9]{4})/(0[1-9]|1[012])/(0[1-9]|[12][0-9]|3[01])$";
final Pattern dateEnPattern = Pattern.compile(DATE_PATTERN_EN);
final Pattern dateFrPattern = Pattern.compile(DATE_PATTERN_FR);

public class DateFormatRegex {
    private String formatPattern;
    private String regexPattern;
    private Locale locale;

    public DateFormatRegex(String format, String regex, Locale locale) {
        this.formatPattern = format;
        this.regexPattern = regex;
        this.locale = locale;
    }

    public String getFormatPattern() {
        return formatPattern;
    }

    public void setFormatPattern(String formatPattern) {
        this.formatPattern = formatPattern;
    }

    public String getRegexPattern() {
        return regexPattern;
    }

    public void setRegexPattern(String regexPattern) {
        this.regexPattern = regexPattern;
    }

    public Locale getLocale() {
        return locale;
    }

    public void setLocale(Locale locale) {
        this.locale = locale;
    }

    public SimpleDateFormat getSimpleDateFormat() {
        return new SimpleDateFormat(this.formatPattern);
    }

    public Pattern getPattern() {
        return Pattern.compile(this.regexPattern);

    }

}

private List<DateFormatRegex> patterns;

public CustomDateFormatter(List<DateFormatRegex> patterns) {
    this.patterns = patterns;
}

public CustomDateFormatter() {
    this.patterns = new ArrayList<>();
    this.patterns.add(new DateFormatRegex("dd/MM/yyyy", DATE_PATTERN_FR, new Locale("fr")));
    this.patterns.add(new DateFormatRegex("yyyy/MM/dd", DATE_PATTERN_EN, new Locale("en")));
};

@Override
public final String print(Date date, Locale locale) {
    if (date == null) {
        return "";
    }
    for (DateFormatRegex dfr : patterns) {
        System.out.println(dfr.getLocale());
        System.out.println(locale.getLanguage());
        if (locale.getLanguage().equals(dfr.getLocale().toString())) {
            try {

                String dateString = dfr.getSimpleDateFormat().format(date);
                System.out
                        .println(("PRINT ==> date :" + date.toString() + " formatString: " + dfr.getFormatPattern()
                                + " date formated : " + dateString));
                return dateString;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    return null;
}

@Override
public Date parse(String text, Locale locale) throws ParseException {
    if (text == null || text.length() == 0) {
        return null;
    }

    for (DateFormatRegex dfr : patterns) {
        if (dfr.getPattern().matcher(text).matches()) {
            try {
                Date date = dfr.getSimpleDateFormat().parse(text);
                System.out.println("PARSE ==> date :" + date.toString() + " formatString: " + dfr.getFormatPattern()
                        + " date sous forme text: " + text);
                return date;
            } catch (ParseException e) {
            }
        }
    }
    return null;
}

After that, I Implement AnnotationFormatterFactory as mentioned at Spring 3 Field Formatting:

public class DateFormatAnnotationFormatterFactory implements AnnotationFormatterFactory<CustomDateFormat> {

@Override
public Set<Class<?>> getFieldTypes() {
    return  new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{java.util.Date.class}));
}

@Override
public Printer<Date> getPrinter(CustomDateFormat annotation, Class<?> fieldType) {
    return getCustomDateFormatter(annotation,fieldType);
}

@Override
public Parser<Date> getParser(CustomDateFormat annotation, Class<?> fieldType) {
    return getCustomDateFormatter(annotation, fieldType);
}

private CustomDateFormatter getCustomDateFormatter(CustomDateFormat annotation, Class<?> fieldType) {

  return new CustomDateFormatter();

}

}

And override method addFormatter() in my class MVCConfiguration:

@Configuration
@ComponentScan
 public class MvcConfiguration implements WebMvcConfigurer {
  @Override
    public void addFormatters(FormatterRegistry registry) {
    DateFormatAnnotationFormatterFactory factory = new DateFormatAnnotationFormatterFactory();
    registry.addFormatterForFieldAnnotation(factory);
    
} }

Finally, I create my custom annotation and use it in Entity actionTracking on field Date creationDate... My problem is that method parse() of my CustomDateFormatter is correctly used : I retrieve my dates in one of two patterns according locale but when I want to display in front-end( using thymeleaf or js after request ajax) date of my entity , method print() is never called and i saw the date in this pattern "yyyy-MM-dd". I don't understand why parsing is correct and not format. May be I miss something to print date in correct pattern. (Just below : Entity, controller, service) Can anybody help me please .Thanks a lot.

@Entity
@Table(name = "actionTracking")
@JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class ActionTracking implements Comparable<Object> {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;

@NotNull
private int name;

private Boolean type;
private String state;
private String priority;
private String theme;
private String origin;

@Column(length = 10000)
@Size(min = 1, message = "Ce champ est obligatoire.")
private String designation;

private String responsible;

@Temporal(TemporalType.DATE)
@CustomDateFormat
@NotNull(message = "Ce champ est obligatoire.")
private Date creationDate; 
 ...

controller

 @GetMapping("/getactions")
 public ResponseEntity<Object> getActions(@PathVariable(value = "missionId") long missionId,
        Authentication authentication) { 
  ...
  List<ActionTracking> linkedActions = actionTrackingService.findActionTracking(mission);

    List<HashMap<Object, Object>> actions = new ArrayList<>();

    if (linkedActions != null && !linkedActions.isEmpty()) {
        for (ActionTracking actionTracking : linkedActions) {

            actions.add(actionTrackingService.toMapV2(actionTracking, user.getLang()));
        }
    }

    HashMap<String, Object> finalResult = new HashMap<>();
    finalResult.put("actions", actions);
  return new ResponseEntity<Object>(finalResult, HttpStatus.OK);
 
 @PostMapping("/add")
  public ResponseEntity<Object> addActionTracking(Model model,
        @PathVariable(value = "missionId") long missionId,
        // @PathVariable(value = "objectId") long objectId,
        @RequestParam(value = "reference") String reference,
        @RequestParam(value = "linkedActions") ArrayList<ActionTracking> linkedActions,
        @RequestParam(value = "riskId") Optional<Long> linkedRiskId,
        @RequestParam(value = "SatisfactionId") Optional<Long> linkedSatisfactionId,
        @Valid ActionTracking actionTracking,
        BindingResult bindingResult, Authentication authentication) {...

service

@Service
public class ActionTrackingService { 
...
public HashMap<Object, Object> toMapV2(ActionTracking actionTracking, String lang) {
    Locale locale = new Locale(lang);

    HashMap<Object, Object> result = new HashMap<>();
    result.put("id", actionTracking.getId());
    result.put("name", actionTracking.getName());
    ...
    result.put("creationDate", actionTracking.getCreationDate());
    result.put("initialDate", actionTracking.getInitialDate());
    result.put("revisedDate", actionTracking.getRevisedDate()); 
    return result;
    }


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source