Skip to content

Commit 4d11346

Browse files
author
Vitaliy
authored
Merge pull request #282 from bohdan-harniuk/89-UI-component-form-generation
89: UI Component form validation of the creation dialog
2 parents 24f5b57 + 2592a1a commit 4d11346

24 files changed

+1032
-255
lines changed

resources/magento2/validation.properties

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
validator.notEmpty={0} must not be empty
1+
validator.notEmpty=The {0} field must not be empty
22
validator.package.validPath=Please specify a valid Magento 2 installation path
33
validator.alphaNumericCharacters={0} must contain letters and numbers only
4+
validator.alphaNumericAndUnderscoreCharacters={0} must contain letters, numbers and underscores only
45
validator.alreadyDeclared={0} is already declared in the {1} module.
56
validator.startWithNumberOrCapitalLetter={0} must start from a number or a capital letter
67
validator.onlyNumbers={0} must contain numbers only
@@ -22,3 +23,6 @@ validator.cronSchedule.invalidExpression={0} has invalid cron schedule expressio
2223
validator.configPath.invalidFormat={0} has invalid config path format (e.g. section/group/field)
2324
validator.moduleNameIsTheSameAsPackage=Module name must be different from the package name
2425
validator.mustNotBeEmptyShouldContainLettersOrNumbers=Must not be empty, should contain letters or numbers
26+
validator.magentoRouteIdInvalid=The route id is invalid
27+
validator.magentoAclResourceIdInvalid=The ACL resource id is invalid
28+
validator.lowercaseCharacters={0} must contain lowercase characters only

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

Lines changed: 155 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,48 @@
55

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

8+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.annotation.FieldValidation;
9+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.annotation.FieldValidations;
10+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.ValidationRule;
811
import com.magento.idea.magento2plugin.bundles.CommonBundle;
12+
import com.magento.idea.magento2plugin.bundles.ValidatorBundle;
913
import java.awt.Dimension;
1014
import java.awt.Toolkit;
15+
import java.lang.reflect.Field;
16+
import java.lang.reflect.InvocationTargetException;
17+
import java.util.ArrayList;
18+
import java.util.Arrays;
19+
import java.util.HashMap;
20+
import java.util.LinkedHashMap;
21+
import java.util.LinkedList;
22+
import java.util.List;
23+
import java.util.Map;
24+
import javax.swing.JComboBox;
25+
import javax.swing.JComponent;
1126
import javax.swing.JDialog;
27+
import javax.swing.JOptionPane;
28+
import javax.swing.JTextField;
1229

1330
/**
1431
* All code generate dialog should extend this class.
1532
*/
1633
@SuppressWarnings({"PMD.ShortVariable", "PMD.MissingSerialVersionUID"})
1734
public abstract class AbstractDialog extends JDialog {
18-
1935
protected CommonBundle bundle;
36+
protected final ValidatorBundle validatorBundle = new ValidatorBundle();
37+
private final String errorTitle;
38+
private final Map<Object, List<ValidationRule>> textFieldValidationRuleMap;
39+
private final Map<Object, Map<ValidationRule, String>> errorMessageFieldValidationRuleMap;
2040

41+
/**
42+
* Abstract Dialog Constructor.
43+
*/
2144
public AbstractDialog() {
2245
super();
23-
this.bundle = new CommonBundle();
46+
bundle = new CommonBundle();
47+
errorTitle = bundle.message("common.error");
48+
textFieldValidationRuleMap = new LinkedHashMap<>();
49+
errorMessageFieldValidationRuleMap = new HashMap<>();
2450
}
2551

2652
protected void centerDialog(final AbstractDialog dialog) {
@@ -33,4 +59,131 @@ protected void centerDialog(final AbstractDialog dialog) {
3359
protected void onCancel() {
3460
this.setVisible(false);
3561
}
62+
63+
protected boolean validateFormFields() {
64+
addValidationRulesFromAnnotations();
65+
for (final Map.Entry<Object, List<ValidationRule>> entry
66+
: textFieldValidationRuleMap.entrySet()) {
67+
final Object field = entry.getKey();
68+
final List<ValidationRule> rules = entry.getValue();
69+
70+
for (final ValidationRule rule : rules) {
71+
final String value = resolveFieldValueByComponentType(field);
72+
73+
if (value != null && !rule.check(value)) {
74+
if (errorMessageFieldValidationRuleMap.containsKey(field)
75+
&& errorMessageFieldValidationRuleMap.get(field).containsKey(rule)) {
76+
showErrorMessage(errorMessageFieldValidationRuleMap.get(field).get(rule));
77+
}
78+
return false;
79+
}
80+
}
81+
}
82+
return true;
83+
}
84+
85+
protected void showErrorMessage(final String errorMessage) {
86+
JOptionPane.showMessageDialog(
87+
null,
88+
errorMessage,
89+
errorTitle,
90+
JOptionPane.ERROR_MESSAGE
91+
);
92+
}
93+
94+
private void addValidationRulesFromAnnotations() {
95+
final Class<?> type = this.getClass();
96+
final List<FieldValidation> validations = new LinkedList<>();
97+
98+
for (final Field field : type.getDeclaredFields()) {
99+
field.setAccessible(true);
100+
validations.clear();
101+
102+
if (field.isAnnotationPresent(FieldValidation.class)) {
103+
validations.add(field.getAnnotation(FieldValidation.class));
104+
}
105+
if (field.isAnnotationPresent(FieldValidations.class)) {
106+
validations.addAll(
107+
Arrays.asList(field.getAnnotation(FieldValidations.class).value())
108+
);
109+
}
110+
111+
for (final FieldValidation validation : validations) {
112+
try {
113+
addValidationRuleToField(
114+
field.get(this),
115+
getRuleFromAnnotation(validation),
116+
getMessageFromAnnotation(validation)
117+
);
118+
} catch (Exception exception) { // NOPMD
119+
// We don't need to cover this case.
120+
}
121+
}
122+
field.setAccessible(false);
123+
}
124+
}
125+
126+
private String getMessageFromAnnotation(final FieldValidation validation) {
127+
String[] params;
128+
final int minMessageArrayLength = 1;
129+
130+
if (validation.message().length > minMessageArrayLength) {
131+
params = Arrays.copyOfRange(validation.message(), 1, validation.message().length);
132+
} else {
133+
params = new String[]{};
134+
}
135+
return validatorBundle.message(validation.message()[0], params);
136+
}
137+
138+
private ValidationRule getRuleFromAnnotation(final FieldValidation validation)
139+
throws NoSuchMethodException,
140+
IllegalAccessException, InvocationTargetException, InstantiationException {
141+
final Class<?> ruleType = validation.rule().getRule();
142+
143+
return (ValidationRule) ruleType.getConstructor().newInstance();
144+
}
145+
146+
protected void addValidationRuleToField(
147+
final Object field,
148+
final ValidationRule rule,
149+
final String message) {
150+
if (!(field instanceof JComponent)) {
151+
return;
152+
}
153+
List<ValidationRule> rules;
154+
if (textFieldValidationRuleMap.containsKey(field)) {
155+
rules = textFieldValidationRuleMap.get(field);
156+
} else {
157+
rules = new ArrayList<>();
158+
}
159+
160+
if (!rules.contains(rule) && rule != null) {
161+
addFieldValidationRuleMessageAssociation(field, rule, message);
162+
rules.add(rule);
163+
textFieldValidationRuleMap.put(field, rules);
164+
}
165+
}
166+
167+
private void addFieldValidationRuleMessageAssociation(
168+
final Object field,
169+
final ValidationRule rule,
170+
final String message) {
171+
Map<ValidationRule, String> validationRuleErrorMessageMap;
172+
if (errorMessageFieldValidationRuleMap.containsKey(field)) {
173+
validationRuleErrorMessageMap = errorMessageFieldValidationRuleMap.get(field);
174+
} else {
175+
validationRuleErrorMessageMap = new HashMap<>();
176+
}
177+
validationRuleErrorMessageMap.put(rule, message);
178+
errorMessageFieldValidationRuleMap.put(field, validationRuleErrorMessageMap);
179+
}
180+
181+
private String resolveFieldValueByComponentType(final Object field) {
182+
if (field instanceof JTextField) {
183+
return ((JTextField) field).getText();
184+
} else if (field instanceof JComboBox) {
185+
return ((JComboBox) field).getSelectedItem().toString();
186+
}
187+
return null;
188+
}
36189
}

src/com/magento/idea/magento2plugin/actions/generation/dialog/NewUiComponentFormDialog.java

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,20 @@
1818
import com.magento.idea.magento2plugin.actions.generation.data.UiComponentFormFieldData;
1919
import com.magento.idea.magento2plugin.actions.generation.data.UiComponentFormFieldsetData;
2020
import com.magento.idea.magento2plugin.actions.generation.data.UiComponentFormFileData;
21-
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.NewUiComponentFormValidator;
21+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.annotation.FieldValidation;
22+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.annotation.RuleRegistry;
23+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.AclResourceIdRule;
24+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.AlphanumericRule;
25+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.DirectoryRule;
26+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.IdentifierRule;
27+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.NotEmptyRule;
28+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.PhpClassRule;
29+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.PhpNamespaceNameRule;
30+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.RouteIdRule;
31+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.StartWithNumberOrCapitalLetterRule;
32+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.ui.component.FormButtonsValidator;
33+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.ui.component.FormFieldsValidator;
34+
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.ui.component.FormFieldsetsValidator;
2235
import com.magento.idea.magento2plugin.actions.generation.generator.LayoutXmlGenerator;
2336
import com.magento.idea.magento2plugin.actions.generation.generator.ModuleControllerClassGenerator;
2437
import com.magento.idea.magento2plugin.actions.generation.generator.RoutesXmlGenerator;
@@ -37,6 +50,7 @@
3750
import com.magento.idea.magento2plugin.ui.table.DeleteRowButton;
3851
import com.magento.idea.magento2plugin.ui.table.TableButton;
3952
import com.magento.idea.magento2plugin.util.magento.GetModuleNameByDirectoryUtil;
53+
import java.awt.Dimension;
4054
import java.awt.event.ActionEvent;
4155
import java.awt.event.ActionListener;
4256
import java.awt.event.KeyEvent;
@@ -65,30 +79,87 @@
6579
"PMD.GodClass"
6680
})
6781
public class NewUiComponentFormDialog extends AbstractDialog {
68-
private final NewUiComponentFormValidator validator;
82+
private final FormButtonsValidator formButtonsValidator;
83+
private final FormFieldsetsValidator formFieldsetsValidator;
84+
private final FormFieldsValidator formFieldsValidator;
6985
private final Project project;
7086
private final String moduleName;
7187
private JPanel contentPane;
7288
private JButton buttonOK;
7389
private JButton buttonCancel;
7490
private FilteredComboBox formAreaSelect;
91+
92+
private static final String VIEW_ACTION_NAME = "View Action Name";
93+
private static final String SUBMIT_ACTION_NAME = "Submit Action Name";
94+
private static final String DATA_PROVIDER_CLASS_NAME = "Data Provider class name";
95+
private static final String DATA_PROVIDER_DIRECTORY = "Data Provider directory";
96+
97+
@FieldValidation(rule = RuleRegistry.NOT_EMPTY, message = {NotEmptyRule.MESSAGE, "Name"})
98+
@FieldValidation(rule = RuleRegistry.IDENTIFIER, message = {IdentifierRule.MESSAGE, "Name"})
7599
private JTextField formName;
100+
101+
@FieldValidation(rule = RuleRegistry.NOT_EMPTY, message = {NotEmptyRule.MESSAGE, "Label"})
76102
private JTextField formLabel;
103+
77104
private JTable formButtons;
78105
private JButton addButton;
79106
private JTable fieldsets;
80107
private JTable fields;
81108
private JButton addFieldset;
82109
private JButton addField;
110+
111+
@FieldValidation(rule = RuleRegistry.ROUTE_ID, message = {RouteIdRule.MESSAGE})
83112
private JTextField route;
113+
114+
@FieldValidation(rule = RuleRegistry.PHP_NAMESPACE_NAME,
115+
message = {PhpNamespaceNameRule.MESSAGE, "View Controller Name"})
84116
private JTextField viewControllerName;
117+
118+
@FieldValidation(rule = RuleRegistry.PHP_CLASS,
119+
message = {PhpClassRule.MESSAGE, VIEW_ACTION_NAME})
120+
@FieldValidation(rule = RuleRegistry.NOT_EMPTY,
121+
message = {NotEmptyRule.MESSAGE, VIEW_ACTION_NAME})
122+
@FieldValidation(rule = RuleRegistry.ALPHANUMERIC,
123+
message = {AlphanumericRule.MESSAGE, VIEW_ACTION_NAME})
124+
@FieldValidation(rule = RuleRegistry.START_WITH_NUMBER_OR_CAPITAL_LETTER,
125+
message = {StartWithNumberOrCapitalLetterRule.MESSAGE, VIEW_ACTION_NAME})
85126
private JTextField viewActionName;
127+
128+
@FieldValidation(rule = RuleRegistry.PHP_NAMESPACE_NAME,
129+
message = {PhpNamespaceNameRule.MESSAGE, "Submit Controller Name"})
86130
private JTextField submitControllerName;
131+
132+
@FieldValidation(rule = RuleRegistry.PHP_CLASS,
133+
message = {PhpClassRule.MESSAGE, SUBMIT_ACTION_NAME})
134+
@FieldValidation(rule = RuleRegistry.NOT_EMPTY,
135+
message = {NotEmptyRule.MESSAGE, SUBMIT_ACTION_NAME})
136+
@FieldValidation(rule = RuleRegistry.ALPHANUMERIC,
137+
message = {AlphanumericRule.MESSAGE, SUBMIT_ACTION_NAME})
138+
@FieldValidation(rule = RuleRegistry.START_WITH_NUMBER_OR_CAPITAL_LETTER,
139+
message = {StartWithNumberOrCapitalLetterRule.MESSAGE, SUBMIT_ACTION_NAME})
87140
private JTextField submitActionName;
141+
142+
@FieldValidation(rule = RuleRegistry.NOT_EMPTY,
143+
message = {NotEmptyRule.MESSAGE, DATA_PROVIDER_CLASS_NAME})
144+
@FieldValidation(rule = RuleRegistry.PHP_CLASS,
145+
message = {PhpClassRule.MESSAGE, DATA_PROVIDER_CLASS_NAME})
146+
@FieldValidation(rule = RuleRegistry.ALPHANUMERIC,
147+
message = {AlphanumericRule.MESSAGE, DATA_PROVIDER_CLASS_NAME})
88148
private JTextField dataProviderClassName;
149+
150+
@FieldValidation(rule = RuleRegistry.NOT_EMPTY,
151+
message = {NotEmptyRule.MESSAGE, DATA_PROVIDER_DIRECTORY})
152+
@FieldValidation(rule = RuleRegistry.DIRECTORY,
153+
message = {DirectoryRule.MESSAGE, DATA_PROVIDER_DIRECTORY})
154+
@FieldValidation(rule = RuleRegistry.START_WITH_NUMBER_OR_CAPITAL_LETTER,
155+
message = {AlphanumericRule.MESSAGE, DATA_PROVIDER_DIRECTORY})
89156
private JTextField dataProviderDirectory;
157+
90158
private JLabel aclLabel;
159+
160+
@FieldValidation(rule = RuleRegistry.ACL_RESOURCE_ID, message = {AclResourceIdRule.MESSAGE})
91161
private JTextField acl;
162+
92163
private JLabel formButtonsLabel;//NOPMD
93164
private JLabel formNameLabel;//NOPMD
94165
private JLabel formLabelLabel;//NOPMD
@@ -127,7 +198,10 @@ public class NewUiComponentFormDialog extends AbstractDialog {
127198
public NewUiComponentFormDialog(final Project project, final PsiDirectory directory) {
128199
super();
129200
this.project = project;
130-
this.validator = new NewUiComponentFormValidator(this);
201+
updateDialogSizeToDefaults();
202+
formButtonsValidator = new FormButtonsValidator(this);
203+
formFieldsetsValidator = new FormFieldsetsValidator(this);
204+
formFieldsValidator = new FormFieldsValidator(this);
131205
this.moduleName = GetModuleNameByDirectoryUtil.execute(directory, project);
132206

133207
setContentPane(contentPane);
@@ -338,7 +412,7 @@ public static void open(final Project project, final PsiDirectory directory) {
338412
}
339413

340414
private void onOK() {
341-
if (!validator.validate()) {
415+
if (!validateFormFields()) {
342416
return;
343417
}
344418

@@ -442,6 +516,7 @@ private PsiFile generateLayoutFile() {
442516
), project).generate(NewUiComponentFormAction.ACTION_NAME, false);
443517
}
444518

519+
@Override
445520
protected void onCancel() {
446521
dispose();
447522
}
@@ -639,4 +714,17 @@ private void toggleAcl() {
639714
acl.setVisible(false);
640715
aclLabel.setVisible(false);
641716
}
717+
718+
@Override
719+
protected boolean validateFormFields() {
720+
return super.validateFormFields()
721+
&& formButtonsValidator.validate()
722+
&& formFieldsetsValidator.validate()
723+
&& formFieldsValidator.validate();
724+
}
725+
726+
private void updateDialogSizeToDefaults() {
727+
final Dimension screenSize = getToolkit().getScreenSize();
728+
setPreferredSize(new Dimension(screenSize.width / 2, screenSize.height / 2));
729+
}
642730
}

0 commit comments

Comments
 (0)