Skip to content

Enhabced validation, added filtering for fields to validate on entity… #524

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,20 @@

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

import com.intellij.openapi.util.Pair;
import com.magento.idea.magento2plugin.actions.generation.dialog.reflection.ExtractComponentFromFieldUtil;
import com.magento.idea.magento2plugin.actions.generation.dialog.util.DialogFieldErrorUtil;
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.annotation.FieldValidation;
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.annotation.FieldValidations;
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.annotation.TypeFieldsRulesParser;
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.data.FieldValidationData;
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.ValidationRule;
import com.magento.idea.magento2plugin.bundles.CommonBundle;
import com.magento.idea.magento2plugin.bundles.ValidatorBundle;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
Expand All @@ -35,17 +31,14 @@
/**
* All code generate dialog should extend this class.
*/
@SuppressWarnings({"PMD.ShortVariable", "PMD.MissingSerialVersionUID"})
public abstract class AbstractDialog extends JDialog {

protected CommonBundle bundle;
protected final ValidatorBundle validatorBundle = new ValidatorBundle();
protected final List<FieldValidationData> fieldsValidationsList;
private final String errorTitle;
private final Map<Field, List<ValidationRule>> textFieldValidationRuleMap;
private final Map<Field, Map<ValidationRule, String>> errorMessageFieldValidationRuleMap;
private JTabbedPane tabbedPane;
private boolean isValidationErrorShown;
private boolean dialogHasErrors;

/**
* Abstract Dialog Constructor.
Expand All @@ -54,15 +47,14 @@ public AbstractDialog() {
super();
bundle = new CommonBundle();
errorTitle = bundle.message("common.error");
textFieldValidationRuleMap = new LinkedHashMap<>();
errorMessageFieldValidationRuleMap = new HashMap<>();
fieldsValidationsList = new TypeFieldsRulesParser(this).parseValidationRules();
}

protected void centerDialog(final AbstractDialog dialog) {
final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
final int x = screenSize.width / 2 - dialog.getSize().width / 2;
final int y = screenSize.height / 2 - dialog.getSize().height / 2;
dialog.setLocation(x, y);
final int coordinateX = screenSize.width / 2 - dialog.getSize().width / 2;
final int coordinateY = screenSize.height / 2 - dialog.getSize().height / 2;
dialog.setLocation(coordinateX, coordinateY);
}

protected void onCancel() {
Expand All @@ -74,48 +66,62 @@ protected void onCancel() {
*
* @return boolean
*/
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.AvoidDeeplyNestedIfStmts"})
protected boolean validateFormFields() {
addValidationRulesFromAnnotations();
boolean dialogHasErrors;
isValidationErrorShown = dialogHasErrors = false;
clearValidationHighlighting();

for (final Map.Entry<Field, List<ValidationRule>> entry
: textFieldValidationRuleMap.entrySet()) {
final Field field = entry.getKey();
final List<ValidationRule> rules = entry.getValue();
for (final FieldValidationData fieldValidationData : getFieldsToValidate()) {
final Field field = fieldValidationData.getField();
final List<Pair<ValidationRule, String>> rules = fieldValidationData.getRules();

for (final ValidationRule rule : rules) {
for (final Pair<ValidationRule, String> rulePair : rules) {
final ValidationRule rule = rulePair.getFirst();
final String message = rulePair.getSecond();
final String value = resolveFieldValueByComponentType(field);

if (value != null && !rule.check(value)) {
if (errorMessageFieldValidationRuleMap.containsKey(field)
&& errorMessageFieldValidationRuleMap.get(field).containsKey(rule)) {
if (!dialogHasErrors) {
final JComponent component = getComponentForField(field);
if (!dialogHasErrors) {
final JComponent component =
ExtractComponentFromFieldUtil.extract(field, this);

if (component != null && tabbedPane != null) {
navigateToTabWithComponent(component);
}
if (component != null && tabbedPane != null) {
navigateToTabWithComponent(component);
}
dialogHasErrors = true;
showErrorMessage(
field,
errorMessageFieldValidationRuleMap.get(field).get(rule)
);
}
dialogHasErrors = true;
showErrorMessage(field, message);
break;
}
}
}

if (dialogHasErrors && !isValidationErrorShown) {
showErrorMessage(
validatorBundle.message("validator.someFieldsHaveErrors")
);
showErrorMessage(validatorBundle.message("validator.someFieldsHaveErrors"));
}

return !dialogHasErrors;
}

/**
* Reset highlighting for fields.
*/
protected void clearValidationHighlighting() {
for (final FieldValidationData fieldValidationData : fieldsValidationsList) {
DialogFieldErrorUtil.resetFieldHighlighting(fieldValidationData.getField(), this);
}
}

/**
* Override this method to change which fields should or shouldn't be validated.
*
* @return List[FieldValidationData]
*/
protected List<FieldValidationData> getFieldsToValidate() {
return new LinkedList<>(fieldsValidationsList);
}

/**
* Tabbed pane should be registered to be possible navigate to the tab in which error occurred.
*
Expand Down Expand Up @@ -162,130 +168,6 @@ protected void showErrorMessage(final String errorMessage) {
isValidationErrorShown = true;
}

/**
* Process validation rules from annotations.
*/
private void addValidationRulesFromAnnotations() {
final Class<?> type = this.getClass();
final List<FieldValidation> validations = new LinkedList<>();

for (final Field field : type.getDeclaredFields()) {
field.setAccessible(true);
validations.clear();

if (field.isAnnotationPresent(FieldValidation.class)) {
validations.add(field.getAnnotation(FieldValidation.class));
}
if (field.isAnnotationPresent(FieldValidations.class)) {
validations.addAll(
Arrays.asList(field.getAnnotation(FieldValidations.class).value())
);
}

for (final FieldValidation validation : validations) {
try {
addValidationRuleToField(
field,
getRuleFromAnnotation(validation),
getMessageFromAnnotation(validation)
);
} catch (NoSuchMethodException | IllegalAccessException
| InvocationTargetException | InstantiationException exception) {
return;
} finally {
field.setAccessible(false);
}
}
field.setAccessible(false);
}
}

/**
* Get error message from annotation.
*
* @param validation FieldValidation
*
* @return String
*/
private String getMessageFromAnnotation(final FieldValidation validation) {
String[] params;
final int minMessageArrayLength = 1;

if (validation.message().length > minMessageArrayLength) {
params = Arrays.copyOfRange(validation.message(), 1, validation.message().length);
} else {
params = new String[]{};
}
return validatorBundle.message(validation.message()[0], (Object[]) params);
}

/**
* Get validation rule from annotation.
*
* @param validation FieldValidation
*
* @return ValidationRule
*/
private ValidationRule getRuleFromAnnotation(final FieldValidation validation)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
InstantiationException {
final Class<?> ruleType = validation.rule().getRule();

return (ValidationRule) ruleType.getConstructor().newInstance();
}

/**
* Add validation rule for field.
*
* @param field Field
* @param rule ValidationRule
* @param message String
*/
protected void addValidationRuleToField(
final Field field,
final ValidationRule rule,
final String message
) {
if (getComponentForField(field) == null) {
return;
}
List<ValidationRule> rules;

if (textFieldValidationRuleMap.containsKey(field)) {
rules = textFieldValidationRuleMap.get(field);
} else {
rules = new ArrayList<>();
}

if (!rules.contains(rule) && rule != null) {
addFieldValidationRuleMessageAssociation(field, rule, message);
rules.add(rule);
textFieldValidationRuleMap.put(field, rules);
}
}

/**
* Associate validation rule with field.
*
* @param field Field
* @param rule ValidationRule
* @param message String
*/
private void addFieldValidationRuleMessageAssociation(
final Field field,
final ValidationRule rule,
final String message
) {
Map<ValidationRule, String> validationRuleErrorMessageMap;
if (errorMessageFieldValidationRuleMap.containsKey(field)) {
validationRuleErrorMessageMap = errorMessageFieldValidationRuleMap.get(field);
} else {
validationRuleErrorMessageMap = new HashMap<>();
}
validationRuleErrorMessageMap.put(rule, message);
errorMessageFieldValidationRuleMap.put(field, validationRuleErrorMessageMap);
}

/**
* Resolve value of stored component by field.
*
Expand All @@ -294,7 +176,7 @@ private void addFieldValidationRuleMessageAssociation(
* @return String
*/
private String resolveFieldValueByComponentType(final Field field) {
final JComponent component = getComponentForField(field);
final JComponent component = ExtractComponentFromFieldUtil.extract(field, this);

if (component instanceof JTextField) {
return ((JTextField) component).isEditable()
Expand All @@ -312,30 +194,6 @@ private String resolveFieldValueByComponentType(final Field field) {
return null;
}

/**
* Get JComponent for field.
*
* @param field Field
*
* @return JComponent
*/
private JComponent getComponentForField(final @NotNull Field field) {
try {
field.setAccessible(true);
final Object component = field.get(this);

if (component instanceof JComponent) {
return (JComponent) component;
}
} catch (IllegalAccessException exception) {
return null;
} finally {
field.setAccessible(false);
}

return null;
}

/**
* Navigate to tab with specified component.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
import com.magento.idea.magento2plugin.actions.generation.data.dialog.EntityCreatorContextData;
import com.magento.idea.magento2plugin.actions.generation.data.dialog.NewEntityDialogData;
import com.magento.idea.magento2plugin.actions.generation.data.ui.ComboBoxItemData;
import com.magento.idea.magento2plugin.actions.generation.dialog.reflection.GetReflectionFieldUtil;
import com.magento.idea.magento2plugin.actions.generation.dialog.util.ClassPropertyFormatterUtil;
import com.magento.idea.magento2plugin.actions.generation.dialog.util.ProcessWorker;
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.annotation.FieldValidation;
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.annotation.RuleRegistry;
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.data.FieldValidationData;
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.AclResourceIdRule;
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.AlphanumericRule;
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.AlphanumericWithUnderscoreRule;
Expand Down Expand Up @@ -57,6 +59,7 @@
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -249,8 +252,9 @@ public void windowClosing(final WindowEvent event) {
onCancel();
}

@SuppressWarnings("PMD.AccessorMethodGeneration")
@Override
public void windowOpened(WindowEvent e) {
public void windowOpened(final WindowEvent event) {
entityName.requestFocus();
}
});
Expand Down Expand Up @@ -300,6 +304,34 @@ public static void open(final Project project, final PsiDirectory directory) {
dialog.setVisible(true);
}

/**
* Filter fields to validate if createUiComponent checkbox isn't selected.
*
* @return List[FieldValidationData]
*/
@Override
protected List<FieldValidationData> getFieldsToValidate() {
final List<FieldValidationData> filteredFields = new LinkedList<>();

if (createUiComponent.isSelected()) {
filteredFields.addAll(super.getFieldsToValidate());
} else {
final List<Field> fieldsToIgnore = new ArrayList<>();
fieldsToIgnore.add(GetReflectionFieldUtil.getByName("route", this.getClass()));
fieldsToIgnore.add(GetReflectionFieldUtil.getByName("formName", this.getClass()));
fieldsToIgnore.add(GetReflectionFieldUtil.getByName("gridName", this.getClass()));

for (final FieldValidationData fieldData : super.getFieldsToValidate()) {
if (fieldsToIgnore.contains(fieldData.getField())) {
continue;
}
filteredFields.add(fieldData);
}
}

return filteredFields;
}

/**
* Initialize combobox sources.
*/
Expand Down
Loading