Skip to content

Commit a3f4ed1

Browse files
Validation enhanced to show all errors above the fields on the form
1 parent 8921267 commit a3f4ed1

File tree

6 files changed

+388
-98
lines changed

6 files changed

+388
-98
lines changed

Diff for: resources/magento2/validation.properties

+1
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,4 @@ validator.lowercaseCharacters={0} must contain lowercase characters only
3636
validator.db.invalidTableNameLength=Table name must contain up to 64 characters only (inclusive)
3737
validator.lowerSnakeCase=The {0} field must be of the lower snake case format
3838
validator.menuIdentifierInvalid=The menu identifier is invalid
39+
validator.someFieldsHaveErrors=Please, check the dialog. Some fields have errors

Diff for: src/com/magento/idea/magento2plugin/actions/generation/dialog/AbstractDialog.java

+142-34
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
package com.magento.idea.magento2plugin.actions.generation.dialog;
77

8-
import com.magento.idea.magento2plugin.actions.generation.dialog.util.HighlightDialogFieldOnErrorUtil;
8+
import com.magento.idea.magento2plugin.actions.generation.dialog.util.DialogFieldErrorUtil;
99
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.annotation.FieldValidation;
1010
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.annotation.FieldValidations;
1111
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.ValidationRule;
@@ -28,17 +28,21 @@
2828
import javax.swing.JOptionPane;
2929
import javax.swing.JTextArea;
3030
import javax.swing.JTextField;
31+
import org.jetbrains.annotations.NotNull;
3132

3233
/**
3334
* All code generate dialog should extend this class.
3435
*/
3536
@SuppressWarnings({"PMD.ShortVariable", "PMD.MissingSerialVersionUID"})
3637
public abstract class AbstractDialog extends JDialog {
38+
3739
protected CommonBundle bundle;
3840
protected final ValidatorBundle validatorBundle = new ValidatorBundle();
3941
private final String errorTitle;
40-
private final Map<Object, List<ValidationRule>> textFieldValidationRuleMap;
41-
private final Map<Object, Map<ValidationRule, String>> errorMessageFieldValidationRuleMap;
42+
private final Map<Field, List<ValidationRule>> textFieldValidationRuleMap;
43+
private final Map<Field, Map<ValidationRule, String>> errorMessageFieldValidationRuleMap;
44+
private boolean isValidationErrorShown;
45+
private boolean dialogHasErrors;
4246

4347
/**
4448
* Abstract Dialog Constructor.
@@ -62,11 +66,18 @@ protected void onCancel() {
6266
this.setVisible(false);
6367
}
6468

69+
/**
70+
* Validate all form fields.
71+
*
72+
* @return boolean
73+
*/
6574
protected boolean validateFormFields() {
6675
addValidationRulesFromAnnotations();
67-
for (final Map.Entry<Object, List<ValidationRule>> entry
76+
isValidationErrorShown = dialogHasErrors = false;
77+
78+
for (final Map.Entry<Field, List<ValidationRule>> entry
6879
: textFieldValidationRuleMap.entrySet()) {
69-
final Object field = entry.getKey();
80+
final Field field = entry.getKey();
7081
final List<ValidationRule> rules = entry.getValue();
7182

7283
for (final ValidationRule rule : rules) {
@@ -75,25 +86,66 @@ protected boolean validateFormFields() {
7586
if (value != null && !rule.check(value)) {
7687
if (errorMessageFieldValidationRuleMap.containsKey(field)
7788
&& errorMessageFieldValidationRuleMap.get(field).containsKey(rule)) {
78-
showErrorMessage(errorMessageFieldValidationRuleMap.get(field).get(rule));
79-
highlightFieldWithErrorStyle(field);
89+
dialogHasErrors = true;
90+
showErrorMessage(
91+
field,
92+
errorMessageFieldValidationRuleMap.get(field).get(rule)
93+
);
8094
}
81-
return false;
95+
break;
8296
}
8397
}
8498
}
85-
return true;
99+
100+
if (dialogHasErrors && !isValidationErrorShown) {
101+
showErrorMessage(
102+
validatorBundle.message("validator.someFieldsHaveErrors")
103+
);
104+
}
105+
106+
return !dialogHasErrors;
86107
}
87108

109+
/**
110+
* Show error message for field.
111+
*
112+
* @param field Field
113+
* @param errorMessage String
114+
*/
115+
protected void showErrorMessage(
116+
final @NotNull Field field,
117+
final @NotNull String errorMessage
118+
) {
119+
final boolean isMessageShown =
120+
DialogFieldErrorUtil.showErrorMessageForField(this, field, errorMessage);
121+
122+
if (!isMessageShown) {
123+
showErrorMessage(errorMessage);
124+
DialogFieldErrorUtil.highlightField(this, field);
125+
}
126+
}
127+
128+
/**
129+
* Show error message in dialog.
130+
*
131+
* @param errorMessage String
132+
*/
88133
protected void showErrorMessage(final String errorMessage) {
134+
if (isValidationErrorShown) {
135+
return;
136+
}
89137
JOptionPane.showMessageDialog(
90138
null,
91139
errorMessage,
92140
errorTitle,
93141
JOptionPane.ERROR_MESSAGE
94142
);
143+
isValidationErrorShown = true;
95144
}
96145

146+
/**
147+
* Process validation rules from annotations.
148+
*/
97149
private void addValidationRulesFromAnnotations() {
98150
final Class<?> type = this.getClass();
99151
final List<FieldValidation> validations = new LinkedList<>();
@@ -114,18 +166,28 @@ private void addValidationRulesFromAnnotations() {
114166
for (final FieldValidation validation : validations) {
115167
try {
116168
addValidationRuleToField(
117-
field.get(this),
169+
field,
118170
getRuleFromAnnotation(validation),
119171
getMessageFromAnnotation(validation)
120172
);
121-
} catch (Exception exception) { // NOPMD
122-
// We don't need to cover this case.
173+
} catch (NoSuchMethodException | IllegalAccessException
174+
| InvocationTargetException | InstantiationException exception) {
175+
return;
176+
} finally {
177+
field.setAccessible(false);
123178
}
124179
}
125180
field.setAccessible(false);
126181
}
127182
}
128183

184+
/**
185+
* Get error message from annotation.
186+
*
187+
* @param validation FieldValidation
188+
*
189+
* @return String
190+
*/
129191
private String getMessageFromAnnotation(final FieldValidation validation) {
130192
String[] params;
131193
final int minMessageArrayLength = 1;
@@ -138,22 +200,38 @@ private String getMessageFromAnnotation(final FieldValidation validation) {
138200
return validatorBundle.message(validation.message()[0], (Object[]) params);
139201
}
140202

203+
/**
204+
* Get validation rule from annotation.
205+
*
206+
* @param validation FieldValidation
207+
*
208+
* @return ValidationRule
209+
*/
141210
private ValidationRule getRuleFromAnnotation(final FieldValidation validation)
142-
throws NoSuchMethodException,
143-
IllegalAccessException, InvocationTargetException, InstantiationException {
211+
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
212+
InstantiationException {
144213
final Class<?> ruleType = validation.rule().getRule();
145214

146215
return (ValidationRule) ruleType.getConstructor().newInstance();
147216
}
148217

218+
/**
219+
* Add validation rule for field.
220+
*
221+
* @param field Field
222+
* @param rule ValidationRule
223+
* @param message String
224+
*/
149225
protected void addValidationRuleToField(
150-
final Object field,
226+
final Field field,
151227
final ValidationRule rule,
152-
final String message) {
153-
if (!(field instanceof JComponent)) {
228+
final String message
229+
) {
230+
if (getComponentForField(field) == null) {
154231
return;
155232
}
156233
List<ValidationRule> rules;
234+
157235
if (textFieldValidationRuleMap.containsKey(field)) {
158236
rules = textFieldValidationRuleMap.get(field);
159237
} else {
@@ -167,10 +245,18 @@ protected void addValidationRuleToField(
167245
}
168246
}
169247

248+
/**
249+
* Associate validation rule with field.
250+
*
251+
* @param field Field
252+
* @param rule ValidationRule
253+
* @param message String
254+
*/
170255
private void addFieldValidationRuleMessageAssociation(
171-
final Object field,
256+
final Field field,
172257
final ValidationRule rule,
173-
final String message) {
258+
final String message
259+
) {
174260
Map<ValidationRule, String> validationRuleErrorMessageMap;
175261
if (errorMessageFieldValidationRuleMap.containsKey(field)) {
176262
validationRuleErrorMessageMap = errorMessageFieldValidationRuleMap.get(field);
@@ -181,31 +267,53 @@ private void addFieldValidationRuleMessageAssociation(
181267
errorMessageFieldValidationRuleMap.put(field, validationRuleErrorMessageMap);
182268
}
183269

184-
private String resolveFieldValueByComponentType(final Object field) {
185-
if (field instanceof JTextField) {
186-
return ((JTextField) field).isEditable() ? ((JTextField) field).getText() : null;
187-
} else if (field instanceof JComboBox) {
188-
if (((JComboBox<?>) field).getSelectedIndex() == -1) {
270+
/**
271+
* Resolve value of stored component by field.
272+
*
273+
* @param field Field
274+
*
275+
* @return String
276+
*/
277+
private String resolveFieldValueByComponentType(final Field field) {
278+
final JComponent component = getComponentForField(field);
279+
280+
if (component instanceof JTextField) {
281+
return ((JTextField) component).isEditable()
282+
? ((JTextField) component).getText() : null;
283+
} else if (component instanceof JComboBox) {
284+
if (((JComboBox<?>) component).getSelectedIndex() == -1) {
189285
return "";
190286
} else {
191-
return ((JComboBox) field).getSelectedItem().toString();
287+
return ((JComboBox) component).getSelectedItem().toString();
192288
}
193-
} else if (field instanceof JTextArea) {
194-
return ((JTextArea) field).getText();
289+
} else if (component instanceof JTextArea) {
290+
return ((JTextArea) component).getText();
195291
}
292+
196293
return null;
197294
}
198295

199296
/**
200-
* Highlight field with error style.
297+
* Get JComponent for field.
298+
*
299+
* @param field Field
201300
*
202-
* @param field Object
301+
* @return JComponent
203302
*/
204-
private void highlightFieldWithErrorStyle(final Object field) {
205-
if (field instanceof JTextField) {
206-
HighlightDialogFieldOnErrorUtil.execute((JTextField) field);
207-
} else if (field instanceof JComboBox) {
208-
HighlightDialogFieldOnErrorUtil.execute((JComboBox) field);
303+
private JComponent getComponentForField(final @NotNull Field field) {
304+
try {
305+
field.setAccessible(true);
306+
final Object component = field.get(this);
307+
308+
if (component instanceof JComponent) {
309+
return (JComponent) component;
310+
}
311+
} catch (IllegalAccessException exception) {
312+
return null;
313+
} finally {
314+
field.setAccessible(false);
209315
}
316+
317+
return null;
210318
}
211319
}

0 commit comments

Comments
 (0)