diff --git a/vaadin-spring/src/main/java/com/vaadin/flow/spring/springnative/VaadinHintsRegistrar.java b/vaadin-spring/src/main/java/com/vaadin/flow/spring/springnative/VaadinHintsRegistrar.java index b140f4d3ce2..1eddc1a9c49 100644 --- a/vaadin-spring/src/main/java/com/vaadin/flow/spring/springnative/VaadinHintsRegistrar.java +++ b/vaadin-spring/src/main/java/com/vaadin/flow/spring/springnative/VaadinHintsRegistrar.java @@ -1,5 +1,14 @@ package com.vaadin.flow.spring.springnative; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import org.reflections.Reflections; +import org.reflections.scanners.Scanners; +import org.reflections.util.ConfigurationBuilder; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.RuntimeHints; @@ -37,6 +46,8 @@ public void registerHints(RuntimeHints hints, ClassLoader classLoader) { } registerResourceIfPresent(hints, "com/vaadin/flow/component/login/i18n.json"); + registerResourceIfPresent(hints, + "com/vaadin/flow/component/crud/i18n.json"); // Flow server resources like BootstrapHandler.js and // RouteNotFoundError_prod.html @@ -54,12 +65,38 @@ private void registerResourceIfPresent(RuntimeHints hints, String path) { // These should really go into the separate components but are here for now // to ease testing - private String[] getCommonComponentClasses() { - return new String[] { "com.vaadin.flow.component.login.LoginI18n", - "com.vaadin.flow.component.login.LoginI18n$Header", - "com.vaadin.flow.component.login.LoginI18n$Form", - "com.vaadin.flow.component.login.LoginI18n$ErrorMessage", - "com.vaadin.flow.component.messages.MessageListItem" }; + private Set getCommonComponentClasses() { + Set classes = new HashSet<>( + List.of("com.vaadin.flow.component.messages.MessageListItem")); + + // A common pattern in Flow components is to handle translations in + // classes with name ending in I18n and their potential inner classes, + // that are serialized as JSON and sent to the client. + // An exception is the Upload component whose translations class has + // capitalized N (UploadI18N) + Predicate i18nClasses = className -> className + .matches(".*I18[nN]($|\\$.*$)"); + // Charts and Map configurations are serialized as JSON to be sent to + // the client. All configuration classes need to be registered for + // reflection. + Predicate componentsFilter = i18nClasses + .or(className -> className + .startsWith("com.vaadin.flow.component.charts.model.") + || className.startsWith( + "com.vaadin.flow.component.map.configuration.")); + + classes.addAll(classesFromPackage("com.vaadin.flow.component", + componentsFilter)); + return classes; + } + + private static Set classesFromPackage(String packageName, + Predicate filter) { + return new Reflections(new ConfigurationBuilder() + .forPackage(packageName).setScanners(Scanners.SubTypes)) + .getAll(Scanners.SubTypes).stream() + .filter(cl -> cl.startsWith(packageName)).filter(filter) + .collect(Collectors.toSet()); } private String[] getClasses() {