1717package com .glebfox .jmix .locstr .action ;
1818
1919import com .glebfox .jmix .locstr .datatype .LocalizedString ;
20+ import com .glebfox .jmix .locstr .validation .BeanValidatorAdapter ;
2021import com .glebfox .jmix .locstr .validation .Validator ;
2122import com .glebfox .jmix .locstr .validation .ValidatorAdapter ;
2223import com .google .common .base .Preconditions ;
2324import com .google .common .base .Strings ;
24- import com .google .common .cache .Cache ;
25- import com .google .common .cache .CacheBuilder ;
2625import com .vaadin .flow .component .*;
2726import com .vaadin .flow .component .button .Button ;
2827import com .vaadin .flow .component .button .ButtonVariant ;
3736import com .vaadin .flow .shared .Registration ;
3837import io .jmix .core .CoreProperties ;
3938import io .jmix .core .MessageTools ;
39+ import io .jmix .core .MetadataTools ;
4040import io .jmix .core .Messages ;
41+ import io .jmix .core .entity .KeyValueEntity ;
42+ import io .jmix .core .metamodel .model .MetaClass ;
4143import io .jmix .core .metamodel .model .MetaProperty ;
4244import io .jmix .core .metamodel .model .MetaPropertyPath ;
4345import io .jmix .flowui .Dialogs ;
4951import io .jmix .flowui .component .PickerComponent ;
5052import io .jmix .flowui .component .SupportsValidation ;
5153import io .jmix .flowui .component .UiComponentUtils ;
54+ import io .jmix .flowui .component .validation .bean .BeanPropertyValidator ;
5255import io .jmix .flowui .data .EntityValueSource ;
5356import io .jmix .flowui .data .ValueSource ;
5457import io .jmix .flowui .kit .action .ActionVariant ;
@@ -78,13 +81,14 @@ public class LocalizedStringEditAction
7881 protected Messages messages ;
7982 protected UiComponents uiComponents ;
8083 protected MessageTools messageTools ;
84+ protected MetadataTools metadataTools ;
8185
8286 protected Dialog dialog ;
8387 protected Button saveButton ;
8488 protected Button cancelButton ;
8589
8690 protected LinkedHashMap <Locale , String > availableLocales ;
87- protected Cache <Locale , HasValueAndElement <?, String >> fieldCache ;
91+ protected Map <Locale , HasValueAndElement <?, String >> fields = new LinkedHashMap <>() ;
8892 protected List <Validator > validators ;
8993
9094 protected Boolean multiline ;
@@ -145,6 +149,11 @@ public void setMessageTools(MessageTools messageTools) {
145149 this .messageTools = messageTools ;
146150 }
147151
152+ @ Autowired
153+ public void setMetadataTools (MetadataTools metadataTools ) {
154+ this .metadataTools = metadataTools ;
155+ }
156+
148157 @ Autowired
149158 public void setDialogs (Dialogs dialogs ) {
150159 this .dialogs = dialogs ;
@@ -177,9 +186,9 @@ public boolean isMultiline() {
177186 *
178187 * @param multiline {@code true} to use a multi-line text
179188 * input component, {@code false} otherwise
180- * @apiNote this setting is applied before the first time the edit
181- * dialog is opened, as fields are cached. {@link TextArea} is used
182- * for multi-line text input, {@link TextField} otherwise
189+ * @apiNote this setting is applied when the edit dialog is opened.
190+ * {@link TextArea} is requested from {@link UiComponents} for
191+ * multi-line text input, {@link TextField} otherwise
183192 */
184193 public void setMultiline (boolean multiline ) {
185194 this .multiline = multiline ;
@@ -192,9 +201,9 @@ public void setMultiline(boolean multiline) {
192201 * @param multiline {@code true} to use a multi-line text
193202 * input component, {@code false} otherwise
194203 * @return this object
195- * @apiNote this setting is applied before the first time the edit
196- * dialog is opened, as fields are cached. {@link TextArea} is used
197- * for multi-line text input, {@link TextField} otherwise
204+ * @apiNote this setting is applied when the edit dialog is opened.
205+ * {@link TextArea} is requested from {@link UiComponents} for
206+ * multi-line text input, {@link TextField} otherwise
198207 */
199208 public LocalizedStringEditAction withMultiline (boolean multiline ) {
200209 setMultiline (multiline );
@@ -786,10 +795,9 @@ public LocalizedStringEditAction withFieldProvider(
786795 public void execute () {
787796 checkTarget ();
788797
789- // Fields are not recreated because they are cached, but
790- // they are correctly initialized with new values
791798 dialog .removeAll ();
792799 dialog .add (createContent ());
800+ updateSaveButtonState ();
793801
794802 // Clear flag after content is created because fields are
795803 // initialized with a default value
@@ -846,8 +854,7 @@ protected void addButtonClickShortcut(Button button, Key key, KeyModifier... key
846854 }
847855
848856 protected void doSave (ClickEvent <Button > event ) {
849- Map <Locale , String > localizedValues = getFields ().asMap ()
850- .entrySet ().stream ()
857+ Map <Locale , String > localizedValues = getFields ().entrySet ().stream ()
851858 .collect (Collectors .toMap (Map .Entry ::getKey ,
852859 entry -> entry .getValue ().getValue ())
853860 );
@@ -892,6 +899,7 @@ protected Component createContent() {
892899 layout .setAlignItems (FlexComponent .Alignment .STRETCH );
893900 layout .setClassName ("localized-string-editor-content" );
894901
902+ fields = new LinkedHashMap <>();
895903 availableLocales .keySet ().stream ()
896904 .map (locale -> ((Component ) getField (locale )))
897905 .forEach (layout ::add );
@@ -900,12 +908,8 @@ protected Component createContent() {
900908 }
901909
902910 protected HasValueAndElement <?, String > getField (Locale locale ) {
903- HasValueAndElement <?, String > field = getFields ().getIfPresent (locale );
904- if (field == null ) {
905- field = createField (locale );
906- getFields ().put (locale , field );
907- }
908-
911+ HasValueAndElement <?, String > field = createField (locale );
912+ getFields ().put (locale , field );
909913 field .setValue (getInitialValue (locale ));
910914 return field ;
911915 }
@@ -944,20 +948,21 @@ protected void initField(HasValueAndElement<?, String> field,
944948 }
945949
946950 initRequired (field , metaPropertyPath );
947- initValidators (field , locale );
951+ initValidators (field , locale , metaPropertyPath );
948952 }
949953
950954 protected void onFieldInvalidChanged (PropertyChangeEvent propertyChangeEvent ) {
951- saveButton . setEnabled (! hasInvalidFields () );
955+ updateSaveButtonState ( );
952956 }
953957
954- protected boolean hasInvalidFields () {
955- return getFields (). asMap ().entrySet ()
958+ protected void updateSaveButtonState () {
959+ boolean hasInvalidFields = getFields ().entrySet ()
956960 .stream ()
957961 .anyMatch (entry ->
958962 entry .getValue () instanceof HasValidation hasValidation
959963 && hasValidation .isInvalid ()
960964 );
965+ saveButton .setEnabled (!hasInvalidFields );
961966 }
962967
963968 protected void initMultilineField (HasValueAndElement <?, String > field ) {
@@ -971,7 +976,6 @@ protected void initMultilineField(HasValueAndElement<?, String> field) {
971976 @ Nullable
972977 protected MetaPropertyPath findMetaPropertyPath () {
973978 ValueSource <LocalizedString > valueSource = target .getValueSource ();
974- MetaPropertyPath metaPropertyPath = null ;
975979 return valueSource instanceof EntityValueSource <?, ?> entityValueSource
976980 ? entityValueSource .getMetaPropertyPath ()
977981 : null ;
@@ -1024,28 +1028,52 @@ protected void initRequired(HasValueAndElement<?, String> field,
10241028 }
10251029
10261030 @ SuppressWarnings ("unchecked" )
1027- protected void initValidators (HasValueAndElement <?, String > field , Locale locale ) {
1031+ protected void initValidators (HasValueAndElement <?, String > field ,
1032+ Locale locale ,
1033+ @ Nullable MetaPropertyPath metaPropertyPath ) {
1034+ if (field instanceof SupportsValidation <?>) {
1035+ initBeanValidator ((SupportsValidation <String >) field , metaPropertyPath );
1036+ }
1037+
10281038 if (validators != null
1029- && field instanceof SupportsValidation ) {
1039+ && field instanceof SupportsValidation <?> ) {
10301040 validators .forEach (validator ->
10311041 ((SupportsValidation <String >) field ).addValidator (new ValidatorAdapter (validator , locale )));
10321042 }
10331043 }
10341044
1045+ protected void initBeanValidator (SupportsValidation <String > field ,
1046+ @ Nullable MetaPropertyPath metaPropertyPath ) {
1047+ if (metaPropertyPath == null ) {
1048+ return ;
1049+ }
1050+
1051+ MetaClass enclosingMetaClass = metadataTools .getPropertyEnclosingMetaClass (metaPropertyPath );
1052+ Class <?> enclosingJavaClass = enclosingMetaClass .getJavaClass ();
1053+ if (enclosingJavaClass == KeyValueEntity .class ) {
1054+ return ;
1055+ }
1056+
1057+ MetaProperty metaProperty = metaPropertyPath .getMetaProperty ();
1058+ if (!LocalizedString .class .isAssignableFrom (metaProperty .getJavaType ())) {
1059+ return ;
1060+ }
1061+
1062+ BeanPropertyValidator beanPropertyValidator = applicationContext .getBean (
1063+ BeanPropertyValidator .class ,
1064+ enclosingJavaClass ,
1065+ metaProperty .getName ());
1066+ field .addValidator (new BeanValidatorAdapter (beanPropertyValidator , availableLocales .keySet ()));
1067+ }
1068+
10351069 @ SuppressWarnings ("unchecked" )
10361070 protected String getInitialValue (Locale locale ) {
10371071 LocalizedString localizedString = ((HasValue <?, LocalizedString >) target ).getValue ();
10381072 return localizedString != null ? localizedString .getValue (locale ) : "" ;
10391073 }
10401074
1041- protected Cache <Locale , HasValueAndElement <?, String >> getFields () {
1042- if (fieldCache == null ) {
1043- fieldCache = CacheBuilder .newBuilder ()
1044- .maximumSize (availableLocales .size ())
1045- .build ();
1046- }
1047-
1048- return fieldCache ;
1075+ protected Map <Locale , HasValueAndElement <?, String >> getFields () {
1076+ return fields ;
10491077 }
10501078
10511079 protected boolean isMac () {
0 commit comments