From 11dbae84b42f4d46195fc51982b27a9963d0c801 Mon Sep 17 00:00:00 2001 From: bohdan-harniuk Date: Mon, 7 Mar 2022 10:20:45 +0200 Subject: [PATCH 1/5] 409: Added arguments injection/replacement action --- resources/META-INF/plugin.xml | 3 + resources/magento2/validation.properties | 4 + .../InjectConstructorArgumentAction.java | 113 +++ .../generation/data/xml/DiArgumentData.java | 100 +++ .../generation/data/xml/DiArrayValueData.java | 235 +++++++ .../generation/dialog/AbstractDialog.java | 12 +- .../dialog/GatherArrayValuesDialog.form | 102 +++ .../dialog/GatherArrayValuesDialog.java | 235 +++++++ .../dialog/NewArgumentInjectionDialog.form | 460 ++++++++++++ .../dialog/NewArgumentInjectionDialog.java | 652 ++++++++++++++++++ .../validator/annotation/RuleRegistry.java | 3 + .../validator/rule/ExtendedNumericRule.java | 24 + .../code/ArgumentInjectionGenerator.java | 219 ++++++ .../code/util/DiXmlTagManipulatorUtil.java | 187 +++++ .../generator/util/CommitXmlFileUtil.java | 25 +- .../magento/packages/DiArgumentType.java | 133 ++++ .../idea/magento2plugin/util/RegExUtil.java | 3 + .../util/php/PhpPsiElementsUtil.java | 18 + 18 files changed, 2525 insertions(+), 3 deletions(-) create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/InjectConstructorArgumentAction.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/data/xml/DiArgumentData.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/data/xml/DiArrayValueData.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/dialog/GatherArrayValuesDialog.form create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/dialog/GatherArrayValuesDialog.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.form create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/rule/ExtendedNumericRule.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/generator/code/ArgumentInjectionGenerator.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/generator/code/util/DiXmlTagManipulatorUtil.java create mode 100644 src/com/magento/idea/magento2plugin/magento/packages/DiArgumentType.java diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index a1fbfdee1..45ba1193a 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -126,6 +126,9 @@ + + + %value%"; + private static final String NULL_VALUE_ITEM_TEMPLATE = + ""; + + private final List items = new ArrayList<>(); + + /** + * Set items data. + * + * @param items List[DiArrayItemData] + */ + public void setItems(final List items) { + this.items.clear(); + this.items.addAll(items); + } + + /** + * Get items data. + * + * @return List[DiArrayItemData] + */ + public List getItems() { + return items; + } + + /** + * Get internal item by its path. + * + * @param path String divided by `:`. + * + * @return DiArrayItemData + */ + public DiArrayItemData getItemByPath(final String path) { + DiArrayItemData target = null; + final String[] parts = path.split(":"); + final String[] left = Arrays.copyOfRange(parts, 1, parts.length); + final String key = parts[0]; + + for (final DiArrayItemData item : getItems()) { + if (item.getName().equals(key)) { + target = item; + + if (left.length > 0) { + final DiArrayValueData childrenHolder = item.getChildren(); + + if (childrenHolder == null) { + break; + } + target = childrenHolder.getItemByPath(String.join(":", left)); + } + break; + } + } + + return target; + } + + @Override + public String toString() { + return buildArrayTree(this, 0); + } + + /** + * Convert to XML. + * + * @param data DiArrayValueData + * + * @return String + */ + public String convertToXml(final DiArrayValueData data) { + final StringBuilder stringBuilder = new StringBuilder(); + + for (final DiArrayItemData item : data.getItems()) { + String value = item.getValue(); + String template = ITEM_TEMPLATE; + + if (item.getType().equals(DiArgumentType.ARRAY) && item.hasChildren()) { + value = "\n" + convertToXml(item.getChildren()) + "\n"; + } else if (item.getType().equals(DiArgumentType.NULL)) { + template = NULL_VALUE_ITEM_TEMPLATE; + } + final String name = item.getName(); + final String type = item.getType().getArgumentType(); + + stringBuilder.append( + template + .replace("%name%", name) + .replace("%type%", type) + .replace("%value%", value) + ); + } + + return stringBuilder.toString(); + } + + private String buildArrayTree(final DiArrayValueData data, final int indentSize) { + final StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.append(indent(0)); + stringBuilder.append("["); + stringBuilder.append("\n"); + int currentItem = 0; + final int itemsCount = data.getItems().size(); + + for (final DiArrayItemData item : data.getItems()) { + stringBuilder.append(indent(indentSize + 1)); + String value = item.getValue(); + + if (item.getType().equals(DiArgumentType.STRING)) { + value = "'" + value + "'"; + } else if (item.getType().equals(DiArgumentType.ARRAY) && item.hasChildren()) { + value = buildArrayTree(item.getChildren(), indentSize + 1); + } else if (item.getType().equals(DiArgumentType.ARRAY) && !item.hasChildren()) { + value = "[]"; + } else if (item.getType().equals(DiArgumentType.BOOLEAN)) { + value = Arrays.asList("1", "true").contains(item.getValue()) ? "true" : "false"; + } else if (item.getType().equals(DiArgumentType.NULL)) { + value = "null"; + } + + stringBuilder + .append("'" + item.getName() + "'") + .append(" => ") + .append(value); + + if (currentItem != itemsCount - 1) { + stringBuilder.append(","); + } + stringBuilder.append("\n"); + currentItem++; + } + stringBuilder.append(indent(indentSize)); + stringBuilder.append("]"); + + return stringBuilder.toString(); + } + + private String indent(final int indentSize) { + return " ".repeat(Math.max(0, indentSize)); + } + + public static class DiArrayItemData { + + private DiArrayValueData children; + private final String name; + private final DiArgumentType type; + private final String value; + + /** + * DiArrayItemData DTO constructor. + * + * @param name String + * @param type DiArgumentType + * @param value DiArgumentType + */ + public DiArrayItemData( + final String name, + final DiArgumentType type, + final String value + ) { + this.name = name; + this.type = type; + this.value = value; + } + + /** + * Check if current item has children. + * + * @return boolean + */ + public boolean hasChildren() { + return children != null && !children.getItems().isEmpty(); + } + + /** + * Get children elements holder. + * + * @return DiArrayValueData + */ + public DiArrayValueData getChildren() { + return children; + } + + /** + * Set children elements holder. + * + * @param children DiArrayValueData + */ + public void setChildren(final DiArrayValueData children) { + this.children = children; + } + + /** + * Get item name. + * + * @return String + */ + public String getName() { + return name; + } + + /** + * Get item type. + * + * @return DiArgumentType + */ + public DiArgumentType getType() { + return type; + } + + /** + * Get item value. + * + * @return String + */ + public String getValue() { + return value; + } + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/AbstractDialog.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/AbstractDialog.java index 395e2793f..1736a00a4 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/dialog/AbstractDialog.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/AbstractDialog.java @@ -6,6 +6,7 @@ package com.magento.idea.magento2plugin.actions.generation.dialog; import com.intellij.openapi.util.Pair; +import com.magento.idea.magento2plugin.actions.generation.data.ui.ComboBoxItemData; import com.magento.idea.magento2plugin.actions.generation.dialog.prompt.PlaceholderInitializerUtil; import com.magento.idea.magento2plugin.actions.generation.dialog.reflection.ExtractComponentFromFieldUtil; import com.magento.idea.magento2plugin.actions.generation.dialog.util.DialogFieldErrorUtil; @@ -209,8 +210,17 @@ private String resolveFieldValueByComponentType(final Field field) { } else if (component instanceof JComboBox) { if (((JComboBox) component).getSelectedIndex() == -1) { return ""; + } + final Object selectedItem = ((JComboBox) component).getSelectedItem(); + + if (selectedItem == null) { + return ""; + } + + if (selectedItem instanceof ComboBoxItemData) { + return ((ComboBoxItemData) selectedItem).getKey(); } else { - return ((JComboBox) component).getSelectedItem().toString(); + return selectedItem.toString(); } } else if (component instanceof JTextArea) { return ((JTextArea) component).getText(); diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/GatherArrayValuesDialog.form b/src/com/magento/idea/magento2plugin/actions/generation/dialog/GatherArrayValuesDialog.form new file mode 100644 index 000000000..12fb4d29c --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/GatherArrayValuesDialog.form @@ -0,0 +1,102 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/GatherArrayValuesDialog.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/GatherArrayValuesDialog.java new file mode 100644 index 000000000..04052a95a --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/GatherArrayValuesDialog.java @@ -0,0 +1,235 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.actions.generation.dialog; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.util.ui.UIUtil; +import com.magento.idea.magento2plugin.actions.generation.InjectConstructorArgumentAction; + +import java.awt.Color; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.KeyStroke; +import javax.swing.table.DefaultTableModel; + +import com.magento.idea.magento2plugin.actions.generation.data.xml.DiArrayValueData; +import com.magento.idea.magento2plugin.bundles.ValidatorBundle; +import com.magento.idea.magento2plugin.magento.packages.DiArgumentType; +import com.magento.idea.magento2plugin.magento.packages.PropertiesTypes; +import com.magento.idea.magento2plugin.ui.table.TableGroupWrapper; +import org.jetbrains.annotations.NotNull; + +public class GatherArrayValuesDialog extends AbstractDialog { + + private static final String ITEM_NAME = "Name"; + private static final String ITEM_TYPE = "Type"; + private static final String ITEM_VALUE = "Value"; + + private final @NotNull Project project; + private DiArrayValueData arrayValueData; + + private TableGroupWrapper tableGroupWrapper; + + private JPanel contentPane; + private JButton buttonCancel; + private JButton buttonOK; + private JPanel itemsPane; + private JScrollPane itemsScrollPane; + private JTable itemsTable; + private JButton buttonAdd; + private JLabel itemsTableErrorMessage; + + /** + * Array values gathering dialog constructor. + * + * @param project Project + * @param arrayValueData DiArrayValueData + */ + public GatherArrayValuesDialog( + final @NotNull Project project, + final DiArrayValueData arrayValueData + ) { + super(); + + this.project = project; + this.arrayValueData = arrayValueData; + + setContentPane(contentPane); + setModal(true); + setTitle(InjectConstructorArgumentAction.GATHER_ARRAY_VALUES_ACTION_DESCRIPTION); + getRootPane().setDefaultButton(buttonOK); + + buttonOK.addActionListener(event -> onOK()); + buttonCancel.addActionListener(event -> onCancel()); + + // call onCancel() when cross is clicked + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(final WindowEvent event) { + onCancel(); + } + }); + + // call onCancel() on ESCAPE + contentPane.registerKeyboardAction( + event -> onCancel(), + KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT + ); + + initTable(); + itemsTableErrorMessage.setVisible(false); + itemsTableErrorMessage.setText(""); + } + + /** + * Open a new array values gathering dialog. + * + * @param project Project + * @param arrayValueData DiArrayValueData + */ + public static void open( + final @NotNull Project project, + final DiArrayValueData arrayValueData + ) { + final GatherArrayValuesDialog dialog = new GatherArrayValuesDialog(project, arrayValueData); + dialog.pack(); + dialog.centerDialog(dialog); + dialog.setVisible(true); + } + + /** + * Fire process if all fields are valid. + */ + private void onOK() { + if (itemsTable.isEditing()) { + itemsTable.getCellEditor().stopCellEditing(); + } + final List items = extractItems(getTableModel()); + final Pair validationResult = validateItems(items); + + if (!validationResult.getFirst()) { + showErrorMessage(validationResult.getSecond()); + return; + } + arrayValueData.setItems(items); + exit(); + } + + @Override + protected void showErrorMessage(final @NotNull String errorMessage) { + itemsTableErrorMessage.setVisible(true); + itemsTableErrorMessage.setFont(UIUtil.getLabelFont(UIUtil.FontSize.SMALL)); + itemsTableErrorMessage.setForeground(new Color(252, 119, 83)); + itemsTableErrorMessage.setText(errorMessage); + } + + private void initTable() { + final List columns = new LinkedList<>(Arrays.asList( + ITEM_NAME, + ITEM_TYPE, + ITEM_VALUE + )); + final Map> sources = new HashMap<>(); + sources.put(ITEM_TYPE, DiArgumentType.getValueList()); + + tableGroupWrapper = new TableGroupWrapper( + itemsTable, + buttonAdd, + columns, + new HashMap<>(), + sources + ); + tableGroupWrapper.initTableGroup(); + } + + private Pair validateItems( + final List items + ) { + final ValidatorBundle validatorBundle = new ValidatorBundle(); + final List itemsNames = new ArrayList<>(); + + for (final DiArrayValueData.DiArrayItemData item : items) { + final DiArgumentType type = item.getType(); + final String value = item.getValue().trim(); + final String name = item.getName().trim(); + + if (name.isEmpty()) { + return new Pair<>( + Boolean.FALSE, + validatorBundle.message("validator.arrayValuesDialog.nameMustNotBeEmpty") + ); + } + itemsNames.add(name); + + final boolean isValid = type.isValid(value); + + if (!isValid) { + return new Pair<>( + Boolean.FALSE, + validatorBundle.message( + "validator.arrayValuesDialog.invalidValueForRowWithName", + value, + name + ) + ); + } + } + + if (itemsNames.stream().distinct().count() != itemsNames.size()) { + return new Pair<>( + Boolean.FALSE, + validatorBundle.message( + "validator.arrayValuesDialog.namesMustBeUnique" + ) + ); + } + + return new Pair<>(Boolean.TRUE, ""); + } + + private List extractItems( + final DefaultTableModel tableModel + ) { + final List items = new ArrayList<>(); + + for (int rowNumber = 0; rowNumber < tableModel.getRowCount(); rowNumber++) { + DiArrayValueData.DiArrayItemData item = new DiArrayValueData.DiArrayItemData( + tableModel.getValueAt(rowNumber, 0).toString(), + DiArgumentType.getByValue(tableModel.getValueAt(rowNumber, 1).toString()), + tableModel.getValueAt(rowNumber, 2).toString().trim() + ); + + items.add(item); + } + + return new ArrayList<>(items); + } + + /** + * Get table model. + * + * @return DefaultTableModel + */ + private DefaultTableModel getTableModel() { + return (DefaultTableModel) itemsTable.getModel(); + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.form b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.form new file mode 100644 index 000000000..01efe99ed --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.form @@ -0,0 +1,460 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.java new file mode 100644 index 000000000..86a4c2092 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.java @@ -0,0 +1,652 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.actions.generation.dialog; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.editor.event.DocumentListener; +import com.intellij.openapi.fileTypes.FileTypes; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.ComboBox; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiFile; +import com.intellij.ui.EditorTextField; +import com.jetbrains.php.PhpIndex; +import com.jetbrains.php.completion.PhpCompletionUtil; +import com.jetbrains.php.lang.psi.elements.Field; +import com.jetbrains.php.lang.psi.elements.Parameter; +import com.jetbrains.php.lang.psi.elements.PhpClass; +import com.jetbrains.php.lang.psi.elements.impl.ClassConstImpl; +import com.magento.idea.magento2plugin.actions.generation.InjectConstructorArgumentAction; +import com.magento.idea.magento2plugin.actions.generation.data.ui.ComboBoxItemData; +import com.magento.idea.magento2plugin.actions.generation.data.xml.DiArgumentData; +import com.magento.idea.magento2plugin.actions.generation.data.xml.DiArrayValueData; +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.rule.ExtendedNumericRule; +import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.NotEmptyRule; +import com.magento.idea.magento2plugin.actions.generation.generator.code.ArgumentInjectionGenerator; +import com.magento.idea.magento2plugin.bundles.ValidatorBundle; +import com.magento.idea.magento2plugin.indexes.ModuleIndex; +import com.magento.idea.magento2plugin.magento.packages.Areas; +import com.magento.idea.magento2plugin.magento.packages.DiArgumentType; +import com.magento.idea.magento2plugin.ui.FilteredComboBox; +import com.magento.idea.magento2plugin.util.magento.GetModuleNameByDirectoryUtil; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class NewArgumentInjectionDialog extends AbstractDialog { + + private static final String TARGET_AREA = "Target Area"; + private static final String TARGET_MODULE = "Target Module"; + private static final String ARGUMENT_TYPE = "Argument Type"; + private static final String ARGUMENT_VALUE = "Argument Value"; + private static final String TYPE_VALUE = "Class/Interface"; + private static final String CONST_VALUE = "Target Constant"; + + private final @NotNull Project project; + private final String moduleName; + private final PhpClass targetClass; + private final Parameter targetParameter; + + private JPanel contentPane; + private JButton buttonCancel; + private JButton buttonOK; + private JTextField targetClassField; + + @FieldValidation(rule = RuleRegistry.NOT_EMPTY, message = {NotEmptyRule.MESSAGE, TARGET_MODULE}) + private FilteredComboBox targetModule; + + @FieldValidation(rule = RuleRegistry.NOT_EMPTY, message = {NotEmptyRule.MESSAGE, TARGET_AREA}) + private JComboBox targetArea; + + @FieldValidation(rule = RuleRegistry.NOT_EMPTY, message = {NotEmptyRule.MESSAGE, ARGUMENT_TYPE}) + private JComboBox argumentType; + + private JTextField targetArgument; + + private JPanel objectValuePane; + private JRadioButton noneRB; + private JRadioButton proxyRB; + private JRadioButton factoryRB; + private EditorTextField objectValue; + private String customObjectValue; + + private JPanel stringValuePane; + + @FieldValidation(rule = RuleRegistry.NOT_EMPTY, + message = {NotEmptyRule.MESSAGE, ARGUMENT_VALUE}) + private JTextField stringValue; + + private JPanel booleanValuePane; + + @FieldValidation(rule = RuleRegistry.NOT_EMPTY, + message = {NotEmptyRule.MESSAGE, ARGUMENT_VALUE}) + private JComboBox booleanValue; + + private JPanel numberValuePane; + + @FieldValidation(rule = RuleRegistry.NOT_EMPTY, + message = {NotEmptyRule.MESSAGE, ARGUMENT_VALUE}) + @FieldValidation(rule = RuleRegistry.EXTENDED_NUMERIC, + message = {ExtendedNumericRule.MESSAGE, ARGUMENT_VALUE}) + private JTextField numberValue; + + private JPanel initParameterValuePane; + + @FieldValidation(rule = RuleRegistry.NOT_EMPTY, message = {NotEmptyRule.MESSAGE, TYPE_VALUE}) + private EditorTextField initParameterTypeValue; + @FieldValidation(rule = RuleRegistry.NOT_EMPTY, message = {NotEmptyRule.MESSAGE, CONST_VALUE}) + private JComboBox initParameterConstValue; + + private JPanel constantValuePane; + + @FieldValidation(rule = RuleRegistry.NOT_EMPTY, message = {NotEmptyRule.MESSAGE, TYPE_VALUE}) + private EditorTextField constantTypeValue; + @FieldValidation(rule = RuleRegistry.NOT_EMPTY, message = {NotEmptyRule.MESSAGE, CONST_VALUE}) + private JComboBox constantValue; + + private JPanel arrayValuePane; + private JComboBox subArrayKey; + private JButton addArrayValueBtn; + private JTextArea arrayView; + + final DiArrayValueData arrayValues; + + // labels + private JLabel argumentTypeLabel;//NOPMD + private JLabel targetModuleLabel;//NOPMD + private JLabel targetAreaLabel;//NOPMD + private JLabel targetClassLabel;//NOPMD + private JLabel targetArgumentLabel;//NOPMD + private JLabel customObjectValueLabel;//NOPMD + private JLabel stringValueLabel;//NOPMD + private JLabel booleanValueLabel;//NOPMD + private JLabel numberValueLabel;//NOPMD + private JLabel initParameterTypeValueLabel;//NOPMD + private JLabel initParameterConstValueLabel;//NOPMD + private JLabel constantTypeValueLabel;//NOPMD + private JLabel constantValueLabel;//NOPMD + private JLabel subArrayKeyLabel;//NOPMD + + /** + * New argument injection dialog constructor. + * + * @param project Project + * @param directory PsiDirectory + * @param targetClass PhpClass + * @param parameter Parameter + */ + public NewArgumentInjectionDialog( + final @NotNull Project project, + final @NotNull PsiDirectory directory, + final @NotNull PhpClass targetClass, + final @NotNull Parameter parameter + ) { + super(); + + this.project = project; + this.targetClass = targetClass; + this.targetParameter = parameter; + this.moduleName = GetModuleNameByDirectoryUtil.execute(directory, project); + arrayValues = new DiArrayValueData(); + + setContentPane(contentPane); + setModal(true); + setTitle(InjectConstructorArgumentAction.ACTION_DESCRIPTION); + getRootPane().setDefaultButton(buttonOK); + + buttonOK.addActionListener(event -> onOK()); + buttonCancel.addActionListener(event -> onCancel()); + + // call onCancel() when cross is clicked + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(final WindowEvent event) { + onCancel(); + } + }); + + // call onCancel() on ESCAPE + contentPane.registerKeyboardAction( + event -> onCancel(), + KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT + ); + + addComponentListener(new FocusOnAFieldListener(() -> targetModule.requestFocusInWindow())); + + targetClassField.setText(targetClass.getPresentableFQN()); + targetArgument.setText(targetParameter.getName()); + + // make all value panes invisible + objectValuePane.setVisible(false); + stringValuePane.setVisible(false); + booleanValuePane.setVisible(false); + numberValuePane.setVisible(false); + initParameterValuePane.setVisible(false); + constantValuePane.setVisible(false); + arrayValuePane.setVisible(false); + + subArrayKeyLabel.setVisible(false); + subArrayKey.setVisible(false); + + objectValue.setEnabled(false); + + argumentType.addItemListener(event -> { + if (!(event.getItem() instanceof ComboBoxItemData)) { + return; + } + final ComboBoxItemData selectedItem = (ComboBoxItemData) event.getItem(); + changeViewBySelectedArgumentType(selectedItem.getKey()); + }); + + final ActionListener objectTypeGroupActionListener = event -> { + if (!(argumentType.getSelectedItem() instanceof ComboBoxItemData)) { + return; + } + final ComboBoxItemData item = (ComboBoxItemData) argumentType.getSelectedItem(); + changeViewBySelectedArgumentType(item.getKey()); + }; + noneRB.addActionListener(objectTypeGroupActionListener); + proxyRB.addActionListener(objectTypeGroupActionListener); + factoryRB.addActionListener(objectTypeGroupActionListener); + + final Disposable disposable = new Disposable() { + @Override + public @NonNls String toString() { + return NewArgumentInjectionDialog.this.toString(); + } + + @Override + public void dispose() { + NewArgumentInjectionDialog.this.dispose(); + } + }; + + PhpCompletionUtil.installClassCompletion( + objectValue, + "", + disposable, + (clazz) -> !clazz.isFinal() + && !PhpCompletionUtil.hasNamespace(clazz, "\\___PHPSTORM_HELPERS") + ); + + objectValue.addDocumentListener(new DocumentListener() { + @Override + public void documentChanged(final @NotNull DocumentEvent event) { + if (noneRB.isSelected()) { + customObjectValue = objectValue.getText().trim(); + } + } + }); + + PhpCompletionUtil.installClassCompletion( + initParameterTypeValue, + "", + disposable, + (clazz) -> !clazz.isFinal() + && !PhpCompletionUtil.hasNamespace(clazz, "\\___PHPSTORM_HELPERS") + ); + + initParameterTypeValue.addDocumentListener( + new DocumentListener() { + @Override + public void documentChanged(final @NotNull DocumentEvent event) { + populateInitParameterTypeConstants(); + } + } + ); + + PhpCompletionUtil.installClassCompletion( + constantTypeValue, + "", + disposable, + (clazz) -> !clazz.isFinal() + && !PhpCompletionUtil.hasNamespace(clazz, "\\___PHPSTORM_HELPERS") + ); + + constantTypeValue.addDocumentListener( + new DocumentListener() { + @Override + public void documentChanged(final @NotNull DocumentEvent event) { + populateConstantTypeConstants(); + } + } + ); + + addArrayValueBtn.addActionListener(event -> { + final DiArrayValueData requestedArrayValues = new DiArrayValueData(); + GatherArrayValuesDialog.open(project, requestedArrayValues); + + if (!requestedArrayValues.getItems().isEmpty()) { + if (arrayValues.getItems().isEmpty()) { + arrayValues.setItems(requestedArrayValues.getItems()); + } else { + if (subArrayKey.getSelectedItem() == null) { + return; + } + final String subArrayName = ((ComboBoxItemData) subArrayKey + .getSelectedItem()).getKey(); + + if (subArrayName.isEmpty()) { + arrayValues.setItems(requestedArrayValues.getItems()); + } else { + final DiArrayValueData.DiArrayItemData parentItem = arrayValues + .getItemByPath( + subArrayName + ); + if (parentItem != null) { + parentItem.setChildren(requestedArrayValues); + } + } + } + populateSubArrayKeyCombobox(); + final String arrayRepresentation = arrayValues.toString(); + + if (!arrayRepresentation.isEmpty()) { + arrayView.setText(arrayRepresentation); + } + } + }); + } + + /** + * Open a new argument injection dialog. + * + * @param project Project + * @param directory PsiDirectory + * @param targetClass PhpClass + * @param parameter Parameter + */ + public static void open( + final @NotNull Project project, + final @NotNull PsiDirectory directory, + final @NotNull PhpClass targetClass, + final @NotNull Parameter parameter + ) { + final NewArgumentInjectionDialog dialog = + new NewArgumentInjectionDialog(project, directory, targetClass, parameter); + dialog.pack(); + dialog.centerDialog(dialog); + dialog.setVisible(true); + } + + /** + * Fire generation process if all fields are valid. + */ + private void onOK() { + if (validateFormFields()) { + final DiArgumentData data = getDialogDataObject(); + + if (data == null) { + return; + } + final ArgumentInjectionGenerator generator = new ArgumentInjectionGenerator( + data, + project + ); + + final PsiFile generatedFile = generator.generate( + InjectConstructorArgumentAction.ACTION_NAME, + true + ); + + if (generatedFile == null) { + if (generator.getGenerationErrorMessage() == null) { + showErrorMessage( + new ValidatorBundle().message( + "validator.file.cantBeCreated", + "DI XML file" + ) + ); + } else { + showErrorMessage( + new ValidatorBundle().message( + "validator.file.cantBeCreatedWithException", + "DI XML file", + generator.getGenerationErrorMessage() + ) + ); + } + } + exit(); + } + } + + /** + * Create custom components and fill their entries. + */ + @SuppressWarnings({"PMD.UnusedPrivateMethod", "PMD.AvoidInstantiatingObjectsInLoops"}) + private void createUIComponents() { + targetModule = new FilteredComboBox(new ModuleIndex(project).getEditableModuleNames()); + targetArea = new ComboBox<>(); + argumentType = new ComboBox<>(); + booleanValue = new ComboBox<>(); + initParameterConstValue = new ComboBox<>(); + constantValue = new ComboBox<>(); + subArrayKey = new ComboBox<>(); + + for (final Areas area : Areas.values()) { + targetArea.addItem(new ComboBoxItemData(area.toString(), area.toString())); + } + + argumentType.addItem( + new ComboBoxItemData("", " --- Select Type --- ") + ); + + for (final DiArgumentType type : DiArgumentType.values()) { + argumentType.addItem( + new ComboBoxItemData(type.getArgumentType(), type.getArgumentType()) + ); + } + objectValue = new EditorTextField("", project, FileTypes.PLAIN_TEXT); + + booleanValue.addItem(new ComboBoxItemData("", " --- Select Value --- ")); + booleanValue.addItem(new ComboBoxItemData("false", "False")); + booleanValue.addItem(new ComboBoxItemData("true", "True")); + + initParameterConstValue.addItem(new ComboBoxItemData("", " --- Select Constant --- ")); + constantValue.addItem(new ComboBoxItemData("", " --- Select Constant --- ")); + + initParameterTypeValue = new EditorTextField("", project, FileTypes.PLAIN_TEXT); + constantTypeValue = new EditorTextField("", project, FileTypes.PLAIN_TEXT); + } + + private void populateInitParameterTypeConstants() { + final String type = initParameterTypeValue.getText().trim(); + + if (type.isEmpty()) { + return; + } + final List constants = getConstantsNames(type); + + if (constants.isEmpty()) { + return; + } + initParameterConstValue.removeAllItems(); + initParameterConstValue.addItem(new ComboBoxItemData("", " --- Select Constant --- ")); + + for (final String constantName : constants) { + initParameterConstValue.addItem(new ComboBoxItemData(constantName, constantName)); + } + } + + private void populateConstantTypeConstants() { + final String type = constantTypeValue.getText().trim(); + + if (type.isEmpty()) { + return; + } + final List constants = getConstantsNames(type); + + if (constants.isEmpty()) { + return; + } + constantValue.removeAllItems(); + constantValue.addItem(new ComboBoxItemData("", " --- Select Constant --- ")); + + for (final String constantName : constants) { + constantValue.addItem(new ComboBoxItemData(constantName, constantName)); + } + } + + private List getConstantsNames(final @NotNull String fqn) { + final List result = new ArrayList<>(); + final PhpIndex phpIndex = PhpIndex.getInstance(project); + + final Collection interfaces = phpIndex.getInterfacesByFQN(fqn); + final Collection classes = phpIndex.getClassesByFQN(fqn); + PhpClass clazz = null; + + if (!interfaces.isEmpty()) { + clazz = interfaces.iterator().next(); + } else if (!classes.isEmpty()) { + clazz = classes.iterator().next(); + } + + if (clazz == null) { + return result; + } + + for (final Field field : clazz.getOwnFields()) { + if (!(field instanceof ClassConstImpl)) { + continue; + } + result.add(field.getName()); + } + + return result; + } + + private void populateSubArrayKeyCombobox() { + if (arrayValues.getItems().isEmpty()) { + subArrayKeyLabel.setVisible(false); + subArrayKey.setVisible(false); + return; + } + subArrayKey.removeAllItems(); + subArrayKey.addItem(new ComboBoxItemData("", " --- Top Array --- ")); + populateSubArrayKeyCombobox(arrayValues, ""); + + if (subArrayKey.getItemCount() > 1) { + subArrayKeyLabel.setVisible(true); + subArrayKey.setVisible(true); + } + } + + private void populateSubArrayKeyCombobox(final DiArrayValueData data, final String parentKey) { + for (final DiArrayValueData.DiArrayItemData item : data.getItems()) { + if (item.getType().equals(DiArgumentType.ARRAY)) { + final String key = parentKey.isEmpty() + ? item.getName() + : parentKey + ":" + item.getName(); + final String value = parentKey.isEmpty() + ? item.getName() + : parentKey.replaceAll(":", " -> ") + " -> " + item.getName(); + + subArrayKey.addItem(new ComboBoxItemData(key, value)); + + if (item.hasChildren()) { + populateSubArrayKeyCombobox(item.getChildren(), key); + } + } + } + } + + private void changeViewBySelectedArgumentType(final String itemValue) { + // make target value pane visible + objectValuePane.setVisible(itemValue.equals(DiArgumentType.OBJECT.getArgumentType())); + stringValuePane.setVisible(itemValue.equals(DiArgumentType.STRING.getArgumentType())); + booleanValuePane.setVisible(itemValue.equals(DiArgumentType.BOOLEAN.getArgumentType())); + numberValuePane.setVisible(itemValue.equals(DiArgumentType.NUMBER.getArgumentType())); + initParameterValuePane.setVisible( + itemValue.equals(DiArgumentType.INIT_PARAMETER.getArgumentType()) + ); + constantValuePane.setVisible(itemValue.equals(DiArgumentType.CONST.getArgumentType())); + arrayValuePane.setVisible(itemValue.equals(DiArgumentType.ARRAY.getArgumentType())); + + if (itemValue.equals(DiArgumentType.OBJECT.getArgumentType())) { + final String targetType = targetClass.getPresentableFQN(); + + if (noneRB.isSelected()) { + if (customObjectValue != null) { + objectValue.setText(customObjectValue); + } + objectValue.setEnabled(true); + } else if (proxyRB.isSelected()) { + objectValue.setEnabled(false); + objectValue.setText(targetType.concat("\\Proxy")); + } else if (factoryRB.isSelected()) { + objectValue.setEnabled(false); + objectValue.setText(targetType.concat("Factory")); + } + } + } + + private @NotNull String getArgumentValue() { + final ComboBoxItemData item = (ComboBoxItemData) argumentType.getSelectedItem(); + + if (item == null) { + return ""; + } + + if (item.getKey().equals(DiArgumentType.OBJECT.getArgumentType())) { + return objectValue.getText().trim(); + } else if (item.getKey().equals(DiArgumentType.STRING.getArgumentType())) { + return stringValue.getText().trim(); + } else if (item.getKey().equals(DiArgumentType.BOOLEAN.getArgumentType())) { + final ComboBoxItemData booleanValueItem = + (ComboBoxItemData) booleanValue.getSelectedItem(); + + if (booleanValueItem == null) { + return ""; + } + return booleanValueItem.getKey(); + } else if (item.getKey().equals(DiArgumentType.NUMBER.getArgumentType())) { + return numberValue.getText().trim(); + } else if (item.getKey().equals(DiArgumentType.INIT_PARAMETER.getArgumentType())) { + final ComboBoxItemData initParamValueItem = + (ComboBoxItemData) initParameterConstValue.getSelectedItem(); + + if (initParamValueItem == null) { + return ""; + } + final String initParamType = initParameterTypeValue.getText().trim(); + + return initParamType.concat("::").concat(initParamValueItem.getKey()); + } else if (item.getKey().equals(DiArgumentType.CONST.getArgumentType())) { + final ComboBoxItemData constValueItem = + (ComboBoxItemData) constantValue.getSelectedItem(); + + if (constValueItem == null) { + return ""; + } + final String constType = constantTypeValue.getText().trim(); + + return constType.concat("::").concat(constValueItem.getKey()); + } else if (item.getKey().equals(DiArgumentType.NULL.getArgumentType())) { + return ""; + } else if (item.getKey().equals(DiArgumentType.ARRAY.getArgumentType())) { + return arrayValues.convertToXml(arrayValues); + } + + return ""; + } + + private DiArgumentData getDialogDataObject() { + if (targetArea.getSelectedItem() == null) { + showErrorMessage(new ValidatorBundle().message(NotEmptyRule.MESSAGE, TARGET_AREA)); + return null; + } + + if (argumentType.getSelectedItem() == null) { + showErrorMessage(new ValidatorBundle().message(NotEmptyRule.MESSAGE, ARGUMENT_TYPE)); + return null; + } + final String argumentTypeValue = ((ComboBoxItemData) argumentType + .getSelectedItem()).getKey().trim(); + + if (argumentTypeValue.isEmpty()) { + showErrorMessage(new ValidatorBundle().message(NotEmptyRule.MESSAGE, ARGUMENT_TYPE)); + return null; + } + + final String argValue = getArgumentValue(); + + if (argValue.isEmpty() + && !argumentTypeValue.equals(DiArgumentType.NULL.getArgumentType())) { + showErrorMessage(new ValidatorBundle().message(NotEmptyRule.MESSAGE, ARGUMENT_VALUE)); + return null; + } + + return new DiArgumentData( + targetModule.getSelectedItem().toString().trim(), + targetClassField.getText().trim(), + targetArgument.getText().trim(), + Areas.getAreaByString(targetArea.getSelectedItem().toString().trim()), + DiArgumentType.getByValue(argumentTypeValue), + argValue + ); + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/annotation/RuleRegistry.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/annotation/RuleRegistry.java index a9a626fdc..057126732 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/annotation/RuleRegistry.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/annotation/RuleRegistry.java @@ -15,6 +15,7 @@ import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.ConfigPathRule; import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.CronScheduleRule; import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.DirectoryRule; +import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.ExtendedNumericRule; import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.IdentifierRule; import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.IdentifierWithColonRule; import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.IdentifierWithForwardSlash; @@ -31,6 +32,7 @@ import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.TableNameLength; public enum RuleRegistry { + NOT_EMPTY(NotEmptyRule.class), BOX_NOT_EMPTY(BoxNotEmptyRule.class), PHP_CLASS(PhpClassRule.class), @@ -53,6 +55,7 @@ public enum RuleRegistry { CONFIG_PATH(ConfigPathRule.class), CLI_COMMAND(CliCommandRule.class), NUMERIC(NumericRule.class), + EXTENDED_NUMERIC(ExtendedNumericRule.class), TABLE_NAME_LENGTH(TableNameLength.class), MENU_IDENTIFIER(MenuIdentifierRule.class); diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/rule/ExtendedNumericRule.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/rule/ExtendedNumericRule.java new file mode 100644 index 000000000..2668cd4a2 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/rule/ExtendedNumericRule.java @@ -0,0 +1,24 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule; + +import com.magento.idea.magento2plugin.util.RegExUtil; +import org.jetbrains.annotations.NotNull; + +public class ExtendedNumericRule implements ValidationRule { + + public static final String MESSAGE = "validator.onlyIntegerOrFloatNumbers"; + private static final ValidationRule INSTANCE = new ExtendedNumericRule(); + + @Override + public boolean check(final @NotNull String value) { + return value.matches(RegExUtil.EXTENDED_NUMERIC); + } + + public static ValidationRule getInstance() { + return INSTANCE; + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/code/ArgumentInjectionGenerator.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/code/ArgumentInjectionGenerator.java new file mode 100644 index 000000000..8aae86fb6 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/code/ArgumentInjectionGenerator.java @@ -0,0 +1,219 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.actions.generation.generator.code; + +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.magento.idea.magento2plugin.actions.generation.data.xml.DiArgumentData; +import com.magento.idea.magento2plugin.actions.generation.generator.FileGenerator; +import com.magento.idea.magento2plugin.actions.generation.generator.code.util.DiXmlTagManipulatorUtil; +import com.magento.idea.magento2plugin.actions.generation.generator.util.DirectoryGenerator; +import com.magento.idea.magento2plugin.actions.generation.generator.util.FileFromTemplateGenerator; +import com.magento.idea.magento2plugin.actions.generation.generator.util.GetCodeTemplateUtil; +import com.magento.idea.magento2plugin.actions.generation.generator.util.XmlFilePositionUtil; +import com.magento.idea.magento2plugin.bundles.CommonBundle; +import com.magento.idea.magento2plugin.bundles.ValidatorBundle; +import com.magento.idea.magento2plugin.indexes.ModuleIndex; +import com.magento.idea.magento2plugin.magento.files.ModuleDiXml; +import com.magento.idea.magento2plugin.magento.packages.Areas; +import com.magento.idea.magento2plugin.magento.packages.Package; +import com.magento.idea.magento2plugin.util.magento.FileBasedIndexUtil; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Properties; +import java.util.stream.Collectors; +import org.jetbrains.annotations.NotNull; + +public final class ArgumentInjectionGenerator extends FileGenerator { + + private final DiArgumentData data; + private final ModuleDiXml file; + private final DirectoryGenerator directoryGenerator; + private final FileFromTemplateGenerator fileFromTemplateGenerator; + private final CommonBundle commonBundle; + private final ValidatorBundle validatorBundle; + private String generationErrorMessage; + + /** + * Argument injection generator constructor. + * + * @param data DiArgumentData + * @param project Project + */ + public ArgumentInjectionGenerator( + final @NotNull DiArgumentData data, + final @NotNull Project project + ) { + super(project); + this.data = data; + file = new ModuleDiXml(); + directoryGenerator = DirectoryGenerator.getInstance(); + fileFromTemplateGenerator = new FileFromTemplateGenerator(project); + commonBundle = new CommonBundle(); + validatorBundle = new ValidatorBundle(); + } + + /** + * Generate Web API XML declaration. + * + * @param actionName String + * + * @return PsiFile + */ + @Override + public PsiFile generate(final @NotNull String actionName) { + final PsiDirectory moduleDirectory = new ModuleIndex(project) + .getModuleDirectoryByModuleName(data.getModuleName()); + + if (moduleDirectory == null) { + generationErrorMessage = "Could not find the target module directory"; + return null; + } + + final XmlFile diXmlFile = getDiXmlFile(actionName, moduleDirectory); + + if (diXmlFile == null) { + if (generationErrorMessage == null) { + generationErrorMessage = "Could not generate the target di.xml file"; + } + return null; + } + + try { + final XmlTag rootTag = diXmlFile.getRootTag(); + + if (rootTag == null) { + generationErrorMessage = "Could not read the target di.xml file"; + return null; + } + final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project); + final Document document = psiDocumentManager.getDocument(diXmlFile); + + if (document == null) { + generationErrorMessage = "Could not get the document for the target di.xml file"; + return null; + } + XmlTag targetTag = getTargetTag(rootTag); + + if (targetTag == null) { + final List> attributes = new ArrayList<>(); + attributes.add(new Pair<>("name", data.getClazz())); + + targetTag = DiXmlTagManipulatorUtil.insertTag( + rootTag, + "type", + attributes + ); + } + + if (targetTag == null) { + return null; + } + DiXmlTagManipulatorUtil.insertArgumentInTypeTag( + targetTag, + data.getParameter(), + data.getValueType(), + data.getValue() + ); + } catch (Exception exception) { + generationErrorMessage = exception.getMessage(); + } + final PsiFile diXmlFileToReformat = diXmlFile; + + WriteCommandAction.runWriteCommandAction(project, () -> { + CodeStyleManager.getInstance(project).reformat(diXmlFileToReformat); + }); + + return diXmlFileToReformat; + } + + /** + * Get generation error message. + * + * @return String + */ + public String getGenerationErrorMessage() { + return generationErrorMessage; + } + + /** + * Fill argument injection values. + * + * @param attributes Properties + */ + @Override + protected void fillAttributes(final @NotNull Properties attributes) { + attributes.put("TYPE", data.getClazz()); + } + + private XmlTag getTargetTag(final @NotNull XmlTag rootTag) { + final Collection tags = PsiTreeUtil.findChildrenOfType(rootTag, XmlTag.class); + final List result = tags.stream() + .filter( + xmlTag -> xmlTag.getName().equals("type") + && xmlTag.getAttributeValue("name") != null + && xmlTag.getAttributeValue("name").equals(data.getClazz()) + ).collect(Collectors.toList()); + + return result.isEmpty() ? null : result.get(0); + } + + private XmlFile getDiXmlFile( + final @NotNull String actionName, + final @NotNull PsiDirectory moduleDirectory + ) { + PsiFile diXmlFile = FileBasedIndexUtil.findModuleConfigFile( + file.getFileName(), + data.getArea(), + data.getModuleName(), + project + ); + + if (diXmlFile == null) { + PsiDirectory diXmlFileDir = null; + + if (data.getArea().equals(Areas.base)) { + diXmlFileDir = directoryGenerator.findOrCreateSubdirectory( + moduleDirectory, + Package.moduleBaseAreaDir + ); + } else { + diXmlFileDir = directoryGenerator.findOrCreateSubdirectories( + moduleDirectory, + String.format( + "%s/%s", + Package.moduleBaseAreaDir, + this.data.getArea().toString() + ) + ); + } + + if (diXmlFileDir == null) { + generationErrorMessage = "Could not locate/generate the target scope directory"; + return null; + } + + diXmlFile = fileFromTemplateGenerator.generate( + file, + new Properties(), + diXmlFileDir, + actionName + ); + } + + return diXmlFile instanceof XmlFile ? (XmlFile) diXmlFile : null; + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/code/util/DiXmlTagManipulatorUtil.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/code/util/DiXmlTagManipulatorUtil.java new file mode 100644 index 000000000..4f81b2373 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/code/util/DiXmlTagManipulatorUtil.java @@ -0,0 +1,187 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.actions.generation.generator.code.util; + +import com.intellij.openapi.util.Pair; +import com.intellij.psi.XmlElementFactory; +import com.intellij.psi.impl.source.xml.XmlTagImpl; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.psi.xml.XmlTagChild; +import com.intellij.psi.xml.XmlTagValue; +import com.intellij.util.xml.DomElement; +import com.intellij.xml.util.XmlPsiUtil; +import com.intellij.xml.util.XmlStringUtil; +import com.intellij.xml.util.XmlTagUtil; +import com.intellij.xml.util.XmlUtil; +import com.magento.idea.magento2plugin.actions.generation.data.xml.DiArrayValueData; +import com.magento.idea.magento2plugin.actions.generation.generator.util.CommitXmlFileUtil; +import com.magento.idea.magento2plugin.magento.files.ModuleDiXml; +import com.magento.idea.magento2plugin.magento.packages.DiArgumentType; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.magento.idea.magento2plugin.util.xml.XmlPsiTreeUtil; +import org.jetbrains.annotations.NotNull; + +public final class DiXmlTagManipulatorUtil { + + /** + * Insert new tag. + * + * @param parentTag XmlTag + * @param tagName String + * @param attributes List[Pair[String, String]] + * + * @return XmlTag + */ + public static XmlTag insertTag( + final @NotNull XmlTag parentTag, + final @NotNull String tagName, + final List> attributes + ) { + XmlTag tag = parentTag.createChildTag( + tagName, + null, + "", + false + ); + + for (final Pair attributeData : attributes) { + tag.setAttribute(attributeData.getFirst(), attributeData.getSecond()); + } + final Map childParentRelationMap = new HashMap<>(); + childParentRelationMap.put(tag, parentTag); + + CommitXmlFileUtil.execute( + (XmlFile) parentTag.getContainingFile(), + List.of(tag), + childParentRelationMap + ); + + for (final XmlTag childTag : PsiTreeUtil.findChildrenOfType(parentTag, XmlTag.class)) { + if (childTag.getText().equals(tag.getText())) { + tag = childTag; + } + } + + return tag; + } + + /** + * Wrap Type/VirtualType argument value into corresponding XML. + * + * @param typeTag XmlTag + * @param name String + * @param type DiArgumentType + * @param value String + */ + public static void insertArgumentInTypeTag( + final @NotNull XmlTag typeTag, + final @NotNull String name, + final @NotNull DiArgumentType type, + final @NotNull String value + ) { + final List addSubTagsQueue = new ArrayList<>(); + final Map childParentRelationMap = new HashMap<>(); + final XmlTag argumentsTag = getOrCreateArgumentsTag( + typeTag, + addSubTagsQueue, + childParentRelationMap + ); + boolean isExists = false; + + for (final XmlTag argTag : PsiTreeUtil.findChildrenOfType(argumentsTag, XmlTag.class)) { + final String argName = argTag.getAttributeValue(ModuleDiXml.NAME_ATTR); + + if (name.equals(argName)) { + CommitXmlFileUtil.execute( + (XmlFile) typeTag.getContainingFile(), + () -> { + argTag.setAttribute(ModuleDiXml.XSI_TYPE_ATTR, type.getArgumentType()); + + if (type.equals(DiArgumentType.ARRAY)) { + argTag.getValue().setEscapedText(""); + String arrayValue = value; + boolean isNotChanged = false; + + while (!arrayValue.isEmpty() && !isNotChanged) { + final XmlTag xmlValueTag = XmlElementFactory.getInstance( + argTag.getProject() + ).createTagFromText(arrayValue); + argTag.addSubTag(xmlValueTag, false); + + final String newArrayValue = arrayValue.replace( + xmlValueTag.getText(), + "" + ); + + if (newArrayValue.equals(arrayValue)) { + isNotChanged = true; + } + arrayValue = newArrayValue; + } + } else { + argTag.getValue().setText(value); + } + + if (value.isEmpty()) { + argTag.collapseIfEmpty(); + } + } + ); + isExists = true; + break; + } + } + + if (!isExists) { + final XmlTag argTag = argumentsTag.createChildTag( + ModuleDiXml.ARGUMENT_TAG, + null, + value.isEmpty() ? null : value, + false + ); + addSubTagsQueue.add(argTag); + childParentRelationMap.put(argTag, argumentsTag); + argTag.setAttribute(ModuleDiXml.NAME_ATTR, name); + argTag.setAttribute(ModuleDiXml.XSI_TYPE_ATTR, type.getArgumentType()); + } + + if (!addSubTagsQueue.isEmpty()) { + CommitXmlFileUtil.execute( + (XmlFile) typeTag.getContainingFile(), + addSubTagsQueue, + childParentRelationMap + ); + } + } + + private static XmlTag getOrCreateArgumentsTag( + final @NotNull XmlTag typeTag, + final List addSubTagsQueue, + final Map childParentRelationMap + ) { + for (final XmlTag tag : PsiTreeUtil.findChildrenOfType(typeTag, XmlTag.class)) { + if (ModuleDiXml.ARGUMENTS_TAG.equals(tag.getName())) { + return tag; + } + } + final XmlTag argumentsTag = typeTag.createChildTag( + ModuleDiXml.ARGUMENTS_TAG, + null, + "", + false + ); + addSubTagsQueue.add(argumentsTag); + childParentRelationMap.put(argumentsTag, typeTag); + + return argumentsTag; + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/util/CommitXmlFileUtil.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/util/CommitXmlFileUtil.java index e450cec31..1b1958532 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/generator/util/CommitXmlFileUtil.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/util/CommitXmlFileUtil.java @@ -29,8 +29,7 @@ private CommitXmlFileUtil() {} public static XmlFile execute( final XmlFile xmlFile, final List subTags, - final Map childParentRelationMap + final Map childParentRelationMap ) { WriteCommandAction.runWriteCommandAction(xmlFile.getProject(), () -> { for (final XmlTag tag : Lists.reverse(subTags)) { @@ -50,4 +49,26 @@ public static XmlFile execute( } return xmlFile; } + + /** + * Make some XML editing operation in the safe env. + * + * @param xmlFile XmlFile + * @param runnable Runnable + */ + public static void execute( + final XmlFile xmlFile, + final Runnable runnable + ) { + WriteCommandAction.runWriteCommandAction(xmlFile.getProject(), runnable); + + final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance( + xmlFile.getProject() + ); + final Document document = psiDocumentManager.getDocument(xmlFile); + + if (document != null) { + psiDocumentManager.commitDocument(document); + } + } } diff --git a/src/com/magento/idea/magento2plugin/magento/packages/DiArgumentType.java b/src/com/magento/idea/magento2plugin/magento/packages/DiArgumentType.java new file mode 100644 index 000000000..e0da3bcfc --- /dev/null +++ b/src/com/magento/idea/magento2plugin/magento/packages/DiArgumentType.java @@ -0,0 +1,133 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.magento.packages; + +import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.ExtendedNumericRule; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.InputMismatchException; +import java.util.List; +import org.jetbrains.annotations.NotNull; + +public enum DiArgumentType { + + OBJECT("object"), + STRING("string"), + BOOLEAN("boolean"), + NUMBER("number"), + INIT_PARAMETER("init_parameter"), + CONST("const"), + NULL("null"), + ARRAY("array"); + + private final String argumentType; + + /** + * Dependency Injection argument types ENUM constructor. + * + * @param argumentType String + */ + DiArgumentType(final String argumentType) { + this.argumentType = argumentType; + } + + /** + * Get argument type. + * + * @return String + */ + public String getArgumentType() { + return argumentType; + } + + /** + * Check if provided value is valid for the current type. + * + * @param value String + * + * @return boolean + */ + public boolean isValid(final @NotNull String value) { + if (getArgumentType().equals(DiArgumentType.STRING.getArgumentType())) { + return validateString(value); + } else if (getArgumentType().equals(DiArgumentType.BOOLEAN.getArgumentType())) { + return validateBoolean(value); + } else if (getArgumentType().equals(DiArgumentType.NUMBER.getArgumentType())) { + return validateNumber(value); + } else if (getArgumentType().equals(DiArgumentType.NULL.getArgumentType())) { + return validateNull(value); + } else if (getArgumentType().equals(DiArgumentType.ARRAY.getArgumentType())) { + return validateArray(value); + } + + return true; + } + + /** + * Get ENUM by its string representation. + * + * @param value String + * + * @return PropertiesTypes + */ + public static DiArgumentType getByValue(final @NotNull String value) { + for (final DiArgumentType type : DiArgumentType.values()) { + if (type.getArgumentType().equals(value)) { + return type; + } + } + + throw new InputMismatchException( + "Invalid argument type value provided. Should be compatible with " + + DiArgumentType.class + ); + } + + /** + * Get argument value list. + * + * @return List[String] + */ + public static List getValueList() { + final List valueList = new ArrayList<>(); + final List simpleTypes = new ArrayList<>(); + + simpleTypes.add(DiArgumentType.STRING); + simpleTypes.add(DiArgumentType.BOOLEAN); + simpleTypes.add(DiArgumentType.NUMBER); + simpleTypes.add(DiArgumentType.NULL); + simpleTypes.add(DiArgumentType.ARRAY); + + for (final DiArgumentType type : DiArgumentType.values()) { + if (!simpleTypes.contains(type)) { + continue; + } + valueList.add(type.getArgumentType()); + } + + return valueList; + } + + private boolean validateString(final @NotNull String value) { + return true; + } + + private boolean validateBoolean(final @NotNull String value) { + return Arrays.asList("true", "false", "1", "0").contains(value); + } + + private boolean validateNumber(final @NotNull String value) { + return ExtendedNumericRule.getInstance().check(value); + } + + private boolean validateNull(final @NotNull String value) { + return true; + } + + private boolean validateArray(final @NotNull String value) { + return true; + } +} diff --git a/src/com/magento/idea/magento2plugin/util/RegExUtil.java b/src/com/magento/idea/magento2plugin/util/RegExUtil.java index 83ce45ed9..8a8794ba7 100644 --- a/src/com/magento/idea/magento2plugin/util/RegExUtil.java +++ b/src/com/magento/idea/magento2plugin/util/RegExUtil.java @@ -25,6 +25,9 @@ public class RegExUtil { public static final String NUMERIC = "[0-9]*"; + public static final String EXTENDED_NUMERIC + = "-?\\d+(\\.\\d+)?"; + public static final String IDENTIFIER = "[a-zA-Z0-9_\\-]*"; diff --git a/src/com/magento/idea/magento2plugin/util/php/PhpPsiElementsUtil.java b/src/com/magento/idea/magento2plugin/util/php/PhpPsiElementsUtil.java index 49dd6b8ba..a2df66c80 100644 --- a/src/com/magento/idea/magento2plugin/util/php/PhpPsiElementsUtil.java +++ b/src/com/magento/idea/magento2plugin/util/php/PhpPsiElementsUtil.java @@ -13,6 +13,7 @@ import com.intellij.psi.impl.source.tree.LeafPsiElement; import com.jetbrains.php.lang.psi.PhpFile; import com.jetbrains.php.lang.psi.elements.Method; +import com.jetbrains.php.lang.psi.elements.Parameter; import com.jetbrains.php.lang.psi.elements.PhpClass; import com.magento.idea.magento2plugin.util.GetFirstClassOfFile; import org.jetbrains.annotations.NotNull; @@ -51,6 +52,23 @@ public static Method getPhpMethod(final @NotNull AnActionEvent event) { return element instanceof Method ? (Method) element : null; } + /** + * Get method argument from event. + * + * @param event AnActionEvent + * + * @return Method + */ + public static Parameter getMethodArgument(final @NotNull AnActionEvent event) { + PsiElement element = getElementUnderCursor(event); + + if (element instanceof LeafPsiElement && element.getParent() instanceof Parameter) { + element = element.getParent(); + } + + return element instanceof Parameter ? (Parameter) element : null; + } + /** * Get php file for event. * From 1ea194e65c56f0b50f870bd394088152a866df9e Mon Sep 17 00:00:00 2001 From: bohdan-harniuk Date: Mon, 7 Mar 2022 10:47:40 +0200 Subject: [PATCH 2/5] 409: Code refactoring --- .../InjectConstructorArgumentAction.java | 9 +---- .../generation/data/xml/DiArrayValueData.java | 26 ++++++------ .../dialog/GatherArrayValuesDialog.java | 30 ++++++-------- .../dialog/NewArgumentInjectionDialog.java | 40 ++++++++++--------- .../code/ArgumentInjectionGenerator.java | 12 +----- .../code/util/DiXmlTagManipulatorUtil.java | 14 ++----- 6 files changed, 56 insertions(+), 75 deletions(-) diff --git a/src/com/magento/idea/magento2plugin/actions/generation/InjectConstructorArgumentAction.java b/src/com/magento/idea/magento2plugin/actions/generation/InjectConstructorArgumentAction.java index 2ec70c4d1..8802b51b2 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/InjectConstructorArgumentAction.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/InjectConstructorArgumentAction.java @@ -8,7 +8,6 @@ import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiDirectory; import com.jetbrains.php.lang.psi.elements.Method; import com.jetbrains.php.lang.psi.elements.Parameter; import com.jetbrains.php.lang.psi.elements.PhpClass; @@ -36,6 +35,7 @@ public InjectConstructorArgumentAction() { } @Override + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) public void update(final @NotNull AnActionEvent event) { setIsAvailableForEvent(event, false); final Project project = event.getProject(); @@ -69,7 +69,7 @@ public void update(final @NotNull AnActionEvent event) { } if (!method.getAccess().isPublic() - || !method.getName().equals(MagentoPhpClass.CONSTRUCT_METHOD_NAME)) { + || !MagentoPhpClass.CONSTRUCT_METHOD_NAME.equals(method.getName())) { return; } currentPhpClass = phpClass; @@ -79,19 +79,14 @@ public void update(final @NotNull AnActionEvent event) { @Override public void actionPerformed(final @NotNull AnActionEvent event) { - final PsiDirectory directory = - currentPhpClass.getContainingFile().getContainingDirectory(); - if (event.getProject() == null || currentPhpClass == null - || directory == null || currentParameter == null) { return; } NewArgumentInjectionDialog.open( event.getProject(), - directory, currentPhpClass, currentParameter ); diff --git a/src/com/magento/idea/magento2plugin/actions/generation/data/xml/DiArrayValueData.java b/src/com/magento/idea/magento2plugin/actions/generation/data/xml/DiArrayValueData.java index ced64aa79..4affc15cd 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/data/xml/DiArrayValueData.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/data/xml/DiArrayValueData.java @@ -58,7 +58,7 @@ public DiArrayItemData getItemByPath(final String path) { if (left.length > 0) { final DiArrayValueData childrenHolder = item.getChildren(); - if (childrenHolder == null) { + if (childrenHolder == null) { // NOPMD break; } target = childrenHolder.getItemByPath(String.join(":", left)); @@ -108,12 +108,12 @@ public String convertToXml(final DiArrayValueData data) { return stringBuilder.toString(); } + @SuppressWarnings({"PMD.CognitiveComplexity", "PMD.CyclomaticComplexity"}) private String buildArrayTree(final DiArrayValueData data, final int indentSize) { final StringBuilder stringBuilder = new StringBuilder(); - - stringBuilder.append(indent(0)); - stringBuilder.append("["); - stringBuilder.append("\n"); + stringBuilder + .append(indent(0)) + .append("[\n"); int currentItem = 0; final int itemsCount = data.getItems().size(); @@ -122,7 +122,7 @@ private String buildArrayTree(final DiArrayValueData data, final int indentSize) String value = item.getValue(); if (item.getType().equals(DiArgumentType.STRING)) { - value = "'" + value + "'"; + value = "'" + value + "'";// NOPMD } else if (item.getType().equals(DiArgumentType.ARRAY) && item.hasChildren()) { value = buildArrayTree(item.getChildren(), indentSize + 1); } else if (item.getType().equals(DiArgumentType.ARRAY) && !item.hasChildren()) { @@ -134,18 +134,20 @@ private String buildArrayTree(final DiArrayValueData data, final int indentSize) } stringBuilder - .append("'" + item.getName() + "'") - .append(" => ") + .append('\'') + .append(item.getName()) + .append("' => ") .append(value); if (currentItem != itemsCount - 1) { - stringBuilder.append(","); + stringBuilder.append(','); } - stringBuilder.append("\n"); + stringBuilder.append('\n'); currentItem++; } - stringBuilder.append(indent(indentSize)); - stringBuilder.append("]"); + stringBuilder + .append(indent(indentSize)) + .append(']'); return stringBuilder.toString(); } diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/GatherArrayValuesDialog.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/GatherArrayValuesDialog.java index 04052a95a..b27a371be 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/dialog/GatherArrayValuesDialog.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/GatherArrayValuesDialog.java @@ -9,7 +9,10 @@ import com.intellij.openapi.util.Pair; import com.intellij.util.ui.UIUtil; import com.magento.idea.magento2plugin.actions.generation.InjectConstructorArgumentAction; - +import com.magento.idea.magento2plugin.actions.generation.data.xml.DiArrayValueData; +import com.magento.idea.magento2plugin.bundles.ValidatorBundle; +import com.magento.idea.magento2plugin.magento.packages.DiArgumentType; +import com.magento.idea.magento2plugin.ui.table.TableGroupWrapper; import java.awt.Color; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; @@ -28,12 +31,6 @@ import javax.swing.JTable; import javax.swing.KeyStroke; import javax.swing.table.DefaultTableModel; - -import com.magento.idea.magento2plugin.actions.generation.data.xml.DiArrayValueData; -import com.magento.idea.magento2plugin.bundles.ValidatorBundle; -import com.magento.idea.magento2plugin.magento.packages.DiArgumentType; -import com.magento.idea.magento2plugin.magento.packages.PropertiesTypes; -import com.magento.idea.magento2plugin.ui.table.TableGroupWrapper; import org.jetbrains.annotations.NotNull; public class GatherArrayValuesDialog extends AbstractDialog { @@ -42,16 +39,14 @@ public class GatherArrayValuesDialog extends AbstractDialog { private static final String ITEM_TYPE = "Type"; private static final String ITEM_VALUE = "Value"; - private final @NotNull Project project; - private DiArrayValueData arrayValueData; - - private TableGroupWrapper tableGroupWrapper; + private final @NotNull Project project;// NOPMD + private final DiArrayValueData arrayValueData; private JPanel contentPane; private JButton buttonCancel; private JButton buttonOK; - private JPanel itemsPane; - private JScrollPane itemsScrollPane; + private JPanel itemsPane;// NOPMD + private JScrollPane itemsScrollPane;// NOPMD private JTable itemsTable; private JButton buttonAdd; private JLabel itemsTableErrorMessage; @@ -151,7 +146,7 @@ private void initTable() { final Map> sources = new HashMap<>(); sources.put(ITEM_TYPE, DiArgumentType.getValueList()); - tableGroupWrapper = new TableGroupWrapper( + final TableGroupWrapper tableGroupWrapper = new TableGroupWrapper( itemsTable, buttonAdd, columns, @@ -168,8 +163,6 @@ private Pair validateItems( final List itemsNames = new ArrayList<>(); for (final DiArrayValueData.DiArrayItemData item : items) { - final DiArgumentType type = item.getType(); - final String value = item.getValue().trim(); final String name = item.getName().trim(); if (name.isEmpty()) { @@ -179,6 +172,8 @@ private Pair validateItems( ); } itemsNames.add(name); + final DiArgumentType type = item.getType(); + final String value = item.getValue().trim(); final boolean isValid = type.isValid(value); @@ -206,13 +201,14 @@ private Pair validateItems( return new Pair<>(Boolean.TRUE, ""); } + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") private List extractItems( final DefaultTableModel tableModel ) { final List items = new ArrayList<>(); for (int rowNumber = 0; rowNumber < tableModel.getRowCount(); rowNumber++) { - DiArrayValueData.DiArrayItemData item = new DiArrayValueData.DiArrayItemData( + final DiArrayValueData.DiArrayItemData item = new DiArrayValueData.DiArrayItemData( tableModel.getValueAt(rowNumber, 0).toString(), DiArgumentType.getByValue(tableModel.getValueAt(rowNumber, 1).toString()), tableModel.getValueAt(rowNumber, 2).toString().trim() diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.java index 86a4c2092..7432b4076 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.java @@ -11,7 +11,6 @@ import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ComboBox; -import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; import com.intellij.ui.EditorTextField; import com.jetbrains.php.PhpIndex; @@ -34,7 +33,6 @@ import com.magento.idea.magento2plugin.magento.packages.Areas; import com.magento.idea.magento2plugin.magento.packages.DiArgumentType; import com.magento.idea.magento2plugin.ui.FilteredComboBox; -import com.magento.idea.magento2plugin.util.magento.GetModuleNameByDirectoryUtil; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; @@ -54,6 +52,12 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +@SuppressWarnings({ + "PMD.TooManyFields", + "PMD.UnusedPrivateField", + "PMD.ExcessiveImports", + "PMD.AvoidInstantiatingObjectsInLoops" +}) public class NewArgumentInjectionDialog extends AbstractDialog { private static final String TARGET_AREA = "Target Area"; @@ -64,9 +68,7 @@ public class NewArgumentInjectionDialog extends AbstractDialog { private static final String CONST_VALUE = "Target Constant"; private final @NotNull Project project; - private final String moduleName; private final PhpClass targetClass; - private final Parameter targetParameter; private JPanel contentPane; private JButton buttonCancel; @@ -130,7 +132,7 @@ public class NewArgumentInjectionDialog extends AbstractDialog { private JButton addArrayValueBtn; private JTextArea arrayView; - final DiArrayValueData arrayValues; + private final DiArrayValueData arrayValues; // labels private JLabel argumentTypeLabel;//NOPMD @@ -152,13 +154,17 @@ public class NewArgumentInjectionDialog extends AbstractDialog { * New argument injection dialog constructor. * * @param project Project - * @param directory PsiDirectory * @param targetClass PhpClass * @param parameter Parameter */ + @SuppressWarnings({ + "PMD.AccessorMethodGeneration", + "PMD.ExcessiveMethodLength", + "PMD.CognitiveComplexity", + "PMD.CyclomaticComplexity", + }) public NewArgumentInjectionDialog( final @NotNull Project project, - final @NotNull PsiDirectory directory, final @NotNull PhpClass targetClass, final @NotNull Parameter parameter ) { @@ -166,8 +172,6 @@ public NewArgumentInjectionDialog( this.project = project; this.targetClass = targetClass; - this.targetParameter = parameter; - this.moduleName = GetModuleNameByDirectoryUtil.execute(directory, project); arrayValues = new DiArrayValueData(); setContentPane(contentPane); @@ -197,7 +201,7 @@ public void windowClosing(final WindowEvent event) { addComponentListener(new FocusOnAFieldListener(() -> targetModule.requestFocusInWindow())); targetClassField.setText(targetClass.getPresentableFQN()); - targetArgument.setText(targetParameter.getName()); + targetArgument.setText(parameter.getName()); // make all value panes invisible objectValuePane.setVisible(false); @@ -335,18 +339,16 @@ public void documentChanged(final @NotNull DocumentEvent event) { * Open a new argument injection dialog. * * @param project Project - * @param directory PsiDirectory * @param targetClass PhpClass * @param parameter Parameter */ public static void open( final @NotNull Project project, - final @NotNull PsiDirectory directory, final @NotNull PhpClass targetClass, final @NotNull Parameter parameter ) { final NewArgumentInjectionDialog dialog = - new NewArgumentInjectionDialog(project, directory, targetClass, parameter); + new NewArgumentInjectionDialog(project, targetClass, parameter); dialog.pack(); dialog.centerDialog(dialog); dialog.setVisible(true); @@ -397,7 +399,7 @@ private void onOK() { /** * Create custom components and fill their entries. */ - @SuppressWarnings({"PMD.UnusedPrivateMethod", "PMD.AvoidInstantiatingObjectsInLoops"}) + @SuppressWarnings({"PMD.UnusedPrivateMethod"}) private void createUIComponents() { targetModule = new FilteredComboBox(new ModuleIndex(project).getEditableModuleNames()); targetArea = new ComboBox<>(); @@ -425,9 +427,10 @@ private void createUIComponents() { booleanValue.addItem(new ComboBoxItemData("", " --- Select Value --- ")); booleanValue.addItem(new ComboBoxItemData("false", "False")); booleanValue.addItem(new ComboBoxItemData("true", "True")); + final String selectConstantText = " --- Select Constant --- "; - initParameterConstValue.addItem(new ComboBoxItemData("", " --- Select Constant --- ")); - constantValue.addItem(new ComboBoxItemData("", " --- Select Constant --- ")); + initParameterConstValue.addItem(new ComboBoxItemData("", selectConstantText)); + constantValue.addItem(new ComboBoxItemData("", selectConstantText)); initParameterTypeValue = new EditorTextField("", project, FileTypes.PLAIN_TEXT); constantTypeValue = new EditorTextField("", project, FileTypes.PLAIN_TEXT); @@ -479,7 +482,7 @@ private List getConstantsNames(final @NotNull String fqn) { final Collection classes = phpIndex.getClassesByFQN(fqn); PhpClass clazz = null; - if (!interfaces.isEmpty()) { + if (!interfaces.isEmpty()) { // NOPMD clazz = interfaces.iterator().next(); } else if (!classes.isEmpty()) { clazz = classes.iterator().next(); @@ -509,7 +512,7 @@ private void populateSubArrayKeyCombobox() { subArrayKey.addItem(new ComboBoxItemData("", " --- Top Array --- ")); populateSubArrayKeyCombobox(arrayValues, ""); - if (subArrayKey.getItemCount() > 1) { + if (subArrayKey.getItemCount() > 1) { // NOPMD subArrayKeyLabel.setVisible(true); subArrayKey.setVisible(true); } @@ -564,6 +567,7 @@ private void changeViewBySelectedArgumentType(final String itemValue) { } } + @SuppressWarnings({"PMD.CognitiveComplexity", "PMD.CyclomaticComplexity"}) private @NotNull String getArgumentValue() { final ComboBoxItemData item = (ComboBoxItemData) argumentType.getSelectedItem(); diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/code/ArgumentInjectionGenerator.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/code/ArgumentInjectionGenerator.java index 8aae86fb6..91e048614 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/generator/code/ArgumentInjectionGenerator.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/code/ArgumentInjectionGenerator.java @@ -21,10 +21,6 @@ import com.magento.idea.magento2plugin.actions.generation.generator.code.util.DiXmlTagManipulatorUtil; import com.magento.idea.magento2plugin.actions.generation.generator.util.DirectoryGenerator; import com.magento.idea.magento2plugin.actions.generation.generator.util.FileFromTemplateGenerator; -import com.magento.idea.magento2plugin.actions.generation.generator.util.GetCodeTemplateUtil; -import com.magento.idea.magento2plugin.actions.generation.generator.util.XmlFilePositionUtil; -import com.magento.idea.magento2plugin.bundles.CommonBundle; -import com.magento.idea.magento2plugin.bundles.ValidatorBundle; import com.magento.idea.magento2plugin.indexes.ModuleIndex; import com.magento.idea.magento2plugin.magento.files.ModuleDiXml; import com.magento.idea.magento2plugin.magento.packages.Areas; @@ -43,8 +39,6 @@ public final class ArgumentInjectionGenerator extends FileGenerator { private final ModuleDiXml file; private final DirectoryGenerator directoryGenerator; private final FileFromTemplateGenerator fileFromTemplateGenerator; - private final CommonBundle commonBundle; - private final ValidatorBundle validatorBundle; private String generationErrorMessage; /** @@ -62,8 +56,6 @@ public ArgumentInjectionGenerator( file = new ModuleDiXml(); directoryGenerator = DirectoryGenerator.getInstance(); fileFromTemplateGenerator = new FileFromTemplateGenerator(project); - commonBundle = new CommonBundle(); - validatorBundle = new ValidatorBundle(); } /** @@ -128,7 +120,7 @@ public PsiFile generate(final @NotNull String actionName) { data.getValueType(), data.getValue() ); - } catch (Exception exception) { + } catch (Exception exception) { // NOPMD generationErrorMessage = exception.getMessage(); } final PsiFile diXmlFileToReformat = diXmlFile; @@ -183,7 +175,7 @@ private XmlFile getDiXmlFile( ); if (diXmlFile == null) { - PsiDirectory diXmlFileDir = null; + PsiDirectory diXmlFileDir; if (data.getArea().equals(Areas.base)) { diXmlFileDir = directoryGenerator.findOrCreateSubdirectory( diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/code/util/DiXmlTagManipulatorUtil.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/code/util/DiXmlTagManipulatorUtil.java index 4f81b2373..c0a83e044 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/generator/code/util/DiXmlTagManipulatorUtil.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/code/util/DiXmlTagManipulatorUtil.java @@ -7,18 +7,9 @@ import com.intellij.openapi.util.Pair; import com.intellij.psi.XmlElementFactory; -import com.intellij.psi.impl.source.xml.XmlTagImpl; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; -import com.intellij.psi.xml.XmlTagChild; -import com.intellij.psi.xml.XmlTagValue; -import com.intellij.util.xml.DomElement; -import com.intellij.xml.util.XmlPsiUtil; -import com.intellij.xml.util.XmlStringUtil; -import com.intellij.xml.util.XmlTagUtil; -import com.intellij.xml.util.XmlUtil; -import com.magento.idea.magento2plugin.actions.generation.data.xml.DiArrayValueData; import com.magento.idea.magento2plugin.actions.generation.generator.util.CommitXmlFileUtil; import com.magento.idea.magento2plugin.magento.files.ModuleDiXml; import com.magento.idea.magento2plugin.magento.packages.DiArgumentType; @@ -26,12 +17,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - -import com.magento.idea.magento2plugin.util.xml.XmlPsiTreeUtil; import org.jetbrains.annotations.NotNull; public final class DiXmlTagManipulatorUtil { + private DiXmlTagManipulatorUtil() {} + /** * Insert new tag. * @@ -82,6 +73,7 @@ public static XmlTag insertTag( * @param type DiArgumentType * @param value String */ + @SuppressWarnings("PMD.CognitiveComplexity") public static void insertArgumentInTypeTag( final @NotNull XmlTag typeTag, final @NotNull String name, From 5d661e6062434aa4c64fb6ef8e0fc09494665220 Mon Sep 17 00:00:00 2001 From: bohdan-harniuk Date: Wed, 9 Mar 2022 11:09:18 +0200 Subject: [PATCH 3/5] 409: Added argument injection/replacement test cases --- .../injectArrayValue/di.xml | 15 + .../injectBooleanValue/di.xml | 9 + .../injectConstValue/di.xml | 9 + .../injectInitParameterValue/di.xml | 9 + .../injectNestedArrayValue/di.xml | 19 + .../injectNullValue/di.xml | 9 + .../injectNumberValue/di.xml | 9 + .../injectObjectValue/di.xml | 9 + .../injectStringValue/di.xml | 9 + .../replaceObjectValueWithFactoryValue/di.xml | 11 + .../replaceObjectValueWithNullValue/di.xml | 11 + .../replaceObjectValueWithProxyValue/di.xml | 11 + .../app/code/Foo/Bar/etc/crontab/di.xml | 11 + .../ArgumentInjectionGeneratorTest.java | 337 ++++++++++++++++++ 14 files changed, 478 insertions(+) create mode 100644 testData/actions/generation/generator/ArgumentInjectionGenerator/injectArrayValue/di.xml create mode 100644 testData/actions/generation/generator/ArgumentInjectionGenerator/injectBooleanValue/di.xml create mode 100644 testData/actions/generation/generator/ArgumentInjectionGenerator/injectConstValue/di.xml create mode 100644 testData/actions/generation/generator/ArgumentInjectionGenerator/injectInitParameterValue/di.xml create mode 100644 testData/actions/generation/generator/ArgumentInjectionGenerator/injectNestedArrayValue/di.xml create mode 100644 testData/actions/generation/generator/ArgumentInjectionGenerator/injectNullValue/di.xml create mode 100644 testData/actions/generation/generator/ArgumentInjectionGenerator/injectNumberValue/di.xml create mode 100644 testData/actions/generation/generator/ArgumentInjectionGenerator/injectObjectValue/di.xml create mode 100644 testData/actions/generation/generator/ArgumentInjectionGenerator/injectStringValue/di.xml create mode 100644 testData/actions/generation/generator/ArgumentInjectionGenerator/replaceObjectValueWithFactoryValue/di.xml create mode 100644 testData/actions/generation/generator/ArgumentInjectionGenerator/replaceObjectValueWithNullValue/di.xml create mode 100644 testData/actions/generation/generator/ArgumentInjectionGenerator/replaceObjectValueWithProxyValue/di.xml create mode 100644 testData/project/magento2/app/code/Foo/Bar/etc/crontab/di.xml create mode 100644 tests/com/magento/idea/magento2plugin/actions/generation/generator/ArgumentInjectionGeneratorTest.java diff --git a/testData/actions/generation/generator/ArgumentInjectionGenerator/injectArrayValue/di.xml b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectArrayValue/di.xml new file mode 100644 index 000000000..0b8341ed4 --- /dev/null +++ b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectArrayValue/di.xml @@ -0,0 +1,15 @@ + + + + + + QW1 + QW2 + QW3 + QW4 + QW5 + + + + diff --git a/testData/actions/generation/generator/ArgumentInjectionGenerator/injectBooleanValue/di.xml b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectBooleanValue/di.xml new file mode 100644 index 000000000..731759df9 --- /dev/null +++ b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectBooleanValue/di.xml @@ -0,0 +1,9 @@ + + + + + false + + + diff --git a/testData/actions/generation/generator/ArgumentInjectionGenerator/injectConstValue/di.xml b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectConstValue/di.xml new file mode 100644 index 000000000..f26db45d3 --- /dev/null +++ b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectConstValue/di.xml @@ -0,0 +1,9 @@ + + + + + Foo\Bar\Model\ServiceTest::DEFAULT_SERVICE + + + diff --git a/testData/actions/generation/generator/ArgumentInjectionGenerator/injectInitParameterValue/di.xml b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectInitParameterValue/di.xml new file mode 100644 index 000000000..f25afb7cb --- /dev/null +++ b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectInitParameterValue/di.xml @@ -0,0 +1,9 @@ + + + + + Foo\Bar\Model\AreaTest::DEFAULT_AREA + + + diff --git a/testData/actions/generation/generator/ArgumentInjectionGenerator/injectNestedArrayValue/di.xml b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectNestedArrayValue/di.xml new file mode 100644 index 000000000..fda440cbd --- /dev/null +++ b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectNestedArrayValue/di.xml @@ -0,0 +1,19 @@ + + + + + + QW1 + QW2 + QW3 + QW4 + + NT1 + true + + + + + + diff --git a/testData/actions/generation/generator/ArgumentInjectionGenerator/injectNullValue/di.xml b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectNullValue/di.xml new file mode 100644 index 000000000..28e6599cd --- /dev/null +++ b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectNullValue/di.xml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/testData/actions/generation/generator/ArgumentInjectionGenerator/injectNumberValue/di.xml b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectNumberValue/di.xml new file mode 100644 index 000000000..1e1cfe5f7 --- /dev/null +++ b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectNumberValue/di.xml @@ -0,0 +1,9 @@ + + + + + 12 + + + diff --git a/testData/actions/generation/generator/ArgumentInjectionGenerator/injectObjectValue/di.xml b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectObjectValue/di.xml new file mode 100644 index 000000000..7c85bc54c --- /dev/null +++ b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectObjectValue/di.xml @@ -0,0 +1,9 @@ + + + + + Foo\Bar\Model\Service + + + diff --git a/testData/actions/generation/generator/ArgumentInjectionGenerator/injectStringValue/di.xml b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectStringValue/di.xml new file mode 100644 index 000000000..b8f8d9d30 --- /dev/null +++ b/testData/actions/generation/generator/ArgumentInjectionGenerator/injectStringValue/di.xml @@ -0,0 +1,9 @@ + + + + + test + + + diff --git a/testData/actions/generation/generator/ArgumentInjectionGenerator/replaceObjectValueWithFactoryValue/di.xml b/testData/actions/generation/generator/ArgumentInjectionGenerator/replaceObjectValueWithFactoryValue/di.xml new file mode 100644 index 000000000..645bbe721 --- /dev/null +++ b/testData/actions/generation/generator/ArgumentInjectionGenerator/replaceObjectValueWithFactoryValue/di.xml @@ -0,0 +1,11 @@ + + + + + 1111 + Foo\Bar\Model\ServiceFactory + Just another argument + + + diff --git a/testData/actions/generation/generator/ArgumentInjectionGenerator/replaceObjectValueWithNullValue/di.xml b/testData/actions/generation/generator/ArgumentInjectionGenerator/replaceObjectValueWithNullValue/di.xml new file mode 100644 index 000000000..206074d73 --- /dev/null +++ b/testData/actions/generation/generator/ArgumentInjectionGenerator/replaceObjectValueWithNullValue/di.xml @@ -0,0 +1,11 @@ + + + + + 1111 + + Just another argument + + + diff --git a/testData/actions/generation/generator/ArgumentInjectionGenerator/replaceObjectValueWithProxyValue/di.xml b/testData/actions/generation/generator/ArgumentInjectionGenerator/replaceObjectValueWithProxyValue/di.xml new file mode 100644 index 000000000..1e708e60a --- /dev/null +++ b/testData/actions/generation/generator/ArgumentInjectionGenerator/replaceObjectValueWithProxyValue/di.xml @@ -0,0 +1,11 @@ + + + + + 1111 + Foo\Bar\Model\Service\Proxy + Just another argument + + + diff --git a/testData/project/magento2/app/code/Foo/Bar/etc/crontab/di.xml b/testData/project/magento2/app/code/Foo/Bar/etc/crontab/di.xml new file mode 100644 index 000000000..55c772245 --- /dev/null +++ b/testData/project/magento2/app/code/Foo/Bar/etc/crontab/di.xml @@ -0,0 +1,11 @@ + + + + + 1111 + Foo\Bar\Model\Service + Just another argument + + + diff --git a/tests/com/magento/idea/magento2plugin/actions/generation/generator/ArgumentInjectionGeneratorTest.java b/tests/com/magento/idea/magento2plugin/actions/generation/generator/ArgumentInjectionGeneratorTest.java new file mode 100644 index 000000000..5f5aec003 --- /dev/null +++ b/tests/com/magento/idea/magento2plugin/actions/generation/generator/ArgumentInjectionGeneratorTest.java @@ -0,0 +1,337 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.actions.generation.generator; + +import com.intellij.psi.PsiFile; +import com.magento.idea.magento2plugin.actions.generation.data.xml.DiArgumentData; +import com.magento.idea.magento2plugin.actions.generation.data.xml.DiArrayValueData; +import com.magento.idea.magento2plugin.actions.generation.generator.code.ArgumentInjectionGenerator; +import com.magento.idea.magento2plugin.magento.files.ModuleDiXml; +import com.magento.idea.magento2plugin.magento.packages.Areas; +import com.magento.idea.magento2plugin.magento.packages.DiArgumentType; +import java.util.ArrayList; +import java.util.List; + +public class ArgumentInjectionGeneratorTest extends BaseGeneratorTestCase { + + private static final String EXPECTED_DIRECTORY = "src/app/code/Foo/Bar/etc/frontend"; + private static final String EXPECTED_DIR_FOR_REPLACING = "src/app/code/Foo/Bar/etc/crontab"; + private static final String MODULE_NAME = "Foo_Bar"; + private static final String TARGET_CLASS = "Foo\\Bar\\Model\\Test"; + private static final String STRING_PARAMETER = "name"; + private static final String STRING_VALUE = "test"; + private static final String BOOL_PARAMETER = "isEmpty"; + private static final String BOOL_VALUE = "false"; + private static final String NUMBER_PARAMETER = "age"; + private static final String NUMBER_VALUE = "12"; + private static final String INIT_PARAM_PARAMETER = "defaultArea"; + private static final String INIT_PARAM_VALUE = "Foo\\Bar\\Model\\AreaTest::DEFAULT_AREA"; + private static final String CONST_PARAMETER = "defaultService"; + private static final String CONST_VALUE = "Foo\\Bar\\Model\\ServiceTest::DEFAULT_SERVICE"; + private static final String NULL_PARAMETER = "object"; + private static final String NULL_VALUE = ""; + private static final String OBJECT_PARAMETER = "object"; + private static final String OBJECT_VALUE = "Foo\\Bar\\Model\\Service"; + private static final String ARRAY_PARAMETER = "methods"; + private static final Areas TEST_AREA = Areas.frontend; + + /** + * Tested string value injection. + */ + public void testInjectStringValue() { + assertGeneratedFileIsCorrect( + myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME)), + EXPECTED_DIRECTORY, + injectConstructorArgument( + new DiArgumentData( + MODULE_NAME, + TARGET_CLASS, + STRING_PARAMETER, + TEST_AREA, + DiArgumentType.STRING, + STRING_VALUE + ) + ) + ); + } + + /** + * Tested boolean value injection. + */ + public void testInjectBooleanValue() { + assertGeneratedFileIsCorrect( + myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME)), + EXPECTED_DIRECTORY, + injectConstructorArgument( + new DiArgumentData( + MODULE_NAME, + TARGET_CLASS, + BOOL_PARAMETER, + TEST_AREA, + DiArgumentType.BOOLEAN, + BOOL_VALUE + ) + ) + ); + } + + /** + * Tested number value injection. + */ + public void testInjectNumberValue() { + assertGeneratedFileIsCorrect( + myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME)), + EXPECTED_DIRECTORY, + injectConstructorArgument( + new DiArgumentData( + MODULE_NAME, + TARGET_CLASS, + NUMBER_PARAMETER, + TEST_AREA, + DiArgumentType.NUMBER, + NUMBER_VALUE + ) + ) + ); + } + + /** + * Tested init_parameter value injection. + */ + public void testInjectInitParameterValue() { + assertGeneratedFileIsCorrect( + myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME)), + EXPECTED_DIRECTORY, + injectConstructorArgument( + new DiArgumentData( + MODULE_NAME, + TARGET_CLASS, + INIT_PARAM_PARAMETER, + TEST_AREA, + DiArgumentType.INIT_PARAMETER, + INIT_PARAM_VALUE + ) + ) + ); + } + + /** + * Tested constant value injection. + */ + public void testInjectConstValue() { + assertGeneratedFileIsCorrect( + myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME)), + EXPECTED_DIRECTORY, + injectConstructorArgument( + new DiArgumentData( + MODULE_NAME, + TARGET_CLASS, + CONST_PARAMETER, + TEST_AREA, + DiArgumentType.CONST, + CONST_VALUE + ) + ) + ); + } + + /** + * Tested null value injection. + */ + public void testInjectNullValue() { + assertGeneratedFileIsCorrect( + myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME)), + EXPECTED_DIRECTORY, + injectConstructorArgument( + new DiArgumentData( + MODULE_NAME, + TARGET_CLASS, + NULL_PARAMETER, + TEST_AREA, + DiArgumentType.NULL, + NULL_VALUE + ) + ) + ); + } + + /** + * Tested object value injection. + */ + public void testInjectObjectValue() { + assertGeneratedFileIsCorrect( + myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME)), + EXPECTED_DIRECTORY, + injectConstructorArgument( + new DiArgumentData( + MODULE_NAME, + TARGET_CLASS, + OBJECT_PARAMETER, + TEST_AREA, + DiArgumentType.OBJECT, + OBJECT_VALUE + ) + ) + ); + } + + /** + * Tested array value injection. + */ + public void testInjectArrayValue() { + assertGeneratedFileIsCorrect( + myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME)), + EXPECTED_DIRECTORY, + injectConstructorArgument( + new DiArgumentData( + MODULE_NAME, + TARGET_CLASS, + ARRAY_PARAMETER, + TEST_AREA, + DiArgumentType.ARRAY, + getArrayValue() + ) + ) + ); + } + + /** + * Tested nested array value injection. + */ + public void testInjectNestedArrayValue() { + assertGeneratedFileIsCorrect( + myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME)), + EXPECTED_DIRECTORY, + injectConstructorArgument( + new DiArgumentData( + MODULE_NAME, + TARGET_CLASS, + ARRAY_PARAMETER, + TEST_AREA, + DiArgumentType.ARRAY, + getNestedArrayValue() + ) + ) + ); + } + + /** + * Tested object value replacing with the null value injection. + */ + public void testReplaceObjectValueWithNullValue() { + assertGeneratedFileIsCorrect( + myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME)), + EXPECTED_DIR_FOR_REPLACING, + injectConstructorArgument( + new DiArgumentData( + MODULE_NAME, + TARGET_CLASS, + OBJECT_PARAMETER, + Areas.crontab, + DiArgumentType.NULL, + NULL_VALUE + ) + ) + ); + } + + /** + * Tested object value replacing with the object proxy value injection. + */ + public void testReplaceObjectValueWithProxyValue() { + assertGeneratedFileIsCorrect( + myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME)), + EXPECTED_DIR_FOR_REPLACING, + injectConstructorArgument( + new DiArgumentData( + MODULE_NAME, + TARGET_CLASS, + OBJECT_PARAMETER, + Areas.crontab, + DiArgumentType.OBJECT, + OBJECT_VALUE + "\\Proxy" + ) + ) + ); + } + + /** + * Tested object value replacing with the object factory value injection. + */ + public void testReplaceObjectValueWithFactoryValue() { + assertGeneratedFileIsCorrect( + myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME)), + EXPECTED_DIR_FOR_REPLACING, + injectConstructorArgument( + new DiArgumentData( + MODULE_NAME, + TARGET_CLASS, + OBJECT_PARAMETER, + Areas.crontab, + DiArgumentType.OBJECT, + OBJECT_VALUE + "Factory" + ) + ) + ); + } + + private PsiFile injectConstructorArgument( + final DiArgumentData data + ) { + final ArgumentInjectionGenerator generator = new ArgumentInjectionGenerator( + data, + myFixture.getProject() + ); + + return generator.generate("test"); + } + + private String getArrayValue() { + final List items = new ArrayList<>(); + items.add(new DiArrayValueData.DiArrayItemData("method1", DiArgumentType.STRING, "QW1")); + items.add(new DiArrayValueData.DiArrayItemData("method2", DiArgumentType.STRING, "QW2")); + items.add(new DiArrayValueData.DiArrayItemData("method3", DiArgumentType.STRING, "QW3")); + items.add(new DiArrayValueData.DiArrayItemData("method4", DiArgumentType.STRING, "QW4")); + items.add(new DiArrayValueData.DiArrayItemData("method5", DiArgumentType.STRING, "QW5")); + final DiArrayValueData arrayValueData = new DiArrayValueData(); + arrayValueData.setItems(items); + + return arrayValueData.convertToXml(arrayValueData); + } + + private String getNestedArrayValue() { + final List items = new ArrayList<>(); + items.add(new DiArrayValueData.DiArrayItemData("method1", DiArgumentType.STRING, "QW1")); + items.add(new DiArrayValueData.DiArrayItemData("method2", DiArgumentType.STRING, "QW2")); + items.add(new DiArrayValueData.DiArrayItemData("method3", DiArgumentType.STRING, "QW3")); + items.add(new DiArrayValueData.DiArrayItemData("method4", DiArgumentType.STRING, "QW4")); + final DiArrayValueData.DiArrayItemData nestedItem = new DiArrayValueData.DiArrayItemData( + "nested", + DiArgumentType.ARRAY, + "" + ); + + final DiArrayValueData nestedItemsHolder = new DiArrayValueData(); + final List nestedItems = new ArrayList<>(); + nestedItems.add( + new DiArrayValueData.DiArrayItemData("nested1", DiArgumentType.STRING, "NT1") + ); + nestedItems.add( + new DiArrayValueData.DiArrayItemData("nested2", DiArgumentType.BOOLEAN, "true") + ); + nestedItems.add( + new DiArrayValueData.DiArrayItemData("nested3", DiArgumentType.NULL, "") + ); + nestedItemsHolder.setItems(nestedItems); + nestedItem.setChildren(nestedItemsHolder); + items.add(nestedItem); + + final DiArrayValueData arrayValueData = new DiArrayValueData(); + arrayValueData.setItems(items); + + return arrayValueData.convertToXml(arrayValueData); + } +} From aeffd6fde50a1e8425d5741320a9fa20fa441bf7 Mon Sep 17 00:00:00 2001 From: bohdan-harniuk Date: Wed, 9 Mar 2022 11:16:20 +0200 Subject: [PATCH 4/5] 409: Suppressed warning --- .../generation/generator/ArgumentInjectionGeneratorTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/com/magento/idea/magento2plugin/actions/generation/generator/ArgumentInjectionGeneratorTest.java b/tests/com/magento/idea/magento2plugin/actions/generation/generator/ArgumentInjectionGeneratorTest.java index 5f5aec003..496a86f5d 100644 --- a/tests/com/magento/idea/magento2plugin/actions/generation/generator/ArgumentInjectionGeneratorTest.java +++ b/tests/com/magento/idea/magento2plugin/actions/generation/generator/ArgumentInjectionGeneratorTest.java @@ -15,6 +15,7 @@ import java.util.ArrayList; import java.util.List; +@SuppressWarnings("PMD.TooManyMethods") public class ArgumentInjectionGeneratorTest extends BaseGeneratorTestCase { private static final String EXPECTED_DIRECTORY = "src/app/code/Foo/Bar/etc/frontend"; From 3293ea116cd8485746cc8825feff763ccc43c81e Mon Sep 17 00:00:00 2001 From: bohdan-harniuk Date: Wed, 9 Mar 2022 11:55:59 +0200 Subject: [PATCH 5/5] 409: Added target type suggestion --- .../dialog/NewArgumentInjectionDialog.java | 39 +++++++++++++++++++ .../util/php/PhpTypeMetadataParserUtil.java | 31 ++++++++++++++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.java index 7432b4076..16dab90f7 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewArgumentInjectionDialog.java @@ -15,6 +15,7 @@ import com.intellij.ui.EditorTextField; import com.jetbrains.php.PhpIndex; import com.jetbrains.php.completion.PhpCompletionUtil; +import com.jetbrains.php.lang.PhpLangUtil; import com.jetbrains.php.lang.psi.elements.Field; import com.jetbrains.php.lang.psi.elements.Parameter; import com.jetbrains.php.lang.psi.elements.PhpClass; @@ -33,11 +34,13 @@ import com.magento.idea.magento2plugin.magento.packages.Areas; import com.magento.idea.magento2plugin.magento.packages.DiArgumentType; import com.magento.idea.magento2plugin.ui.FilteredComboBox; +import com.magento.idea.magento2plugin.util.php.PhpTypeMetadataParserUtil; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import javax.swing.JButton; @@ -69,6 +72,7 @@ public class NewArgumentInjectionDialog extends AbstractDialog { private final @NotNull Project project; private final PhpClass targetClass; + private final Parameter targetParameter; private JPanel contentPane; private JButton buttonCancel; @@ -172,6 +176,7 @@ public NewArgumentInjectionDialog( this.project = project; this.targetClass = targetClass; + targetParameter = parameter; arrayValues = new DiArrayValueData(); setContentPane(contentPane); @@ -333,6 +338,7 @@ public void documentChanged(final @NotNull DocumentEvent event) { } } }); + guessTargetType(); } /** @@ -618,6 +624,39 @@ private void changeViewBySelectedArgumentType(final String itemValue) { return ""; } + @SuppressWarnings("PMD.CyclomaticComplexity") + private void guessTargetType() { + final String mainType = PhpTypeMetadataParserUtil.getMainType(targetParameter); + + if (mainType == null) { + return; + } + String targetDiType = ""; + + if (Arrays.asList("int", "float").contains(mainType)) { + targetDiType = DiArgumentType.NUMBER.getArgumentType(); + } else if (DiArgumentType.STRING.getArgumentType().equals(mainType)) { + targetDiType = DiArgumentType.STRING.getArgumentType(); + } else if ("bool".equals(mainType)) { + targetDiType = DiArgumentType.BOOLEAN.getArgumentType(); + } else if (PhpLangUtil.isFqn(mainType)) { + targetDiType = DiArgumentType.OBJECT.getArgumentType(); + } else if ("array".equals(mainType)) { + targetDiType = DiArgumentType.ARRAY.getArgumentType(); + } + + if (targetDiType.isEmpty()) { + return; + } + + for (int i = 0; i < argumentType.getItemCount(); i++) { + if (targetDiType.equals(argumentType.getItemAt(i).getKey())) { + argumentType.setSelectedIndex(i); + break; + } + } + } + private DiArgumentData getDialogDataObject() { if (targetArea.getSelectedItem() == null) { showErrorMessage(new ValidatorBundle().message(NotEmptyRule.MESSAGE, TARGET_AREA)); diff --git a/src/com/magento/idea/magento2plugin/util/php/PhpTypeMetadataParserUtil.java b/src/com/magento/idea/magento2plugin/util/php/PhpTypeMetadataParserUtil.java index 6f8cec22f..9ea2c4941 100644 --- a/src/com/magento/idea/magento2plugin/util/php/PhpTypeMetadataParserUtil.java +++ b/src/com/magento/idea/magento2plugin/util/php/PhpTypeMetadataParserUtil.java @@ -135,7 +135,12 @@ public static List getMethodsByNames( * * @return String */ - @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity", "PMD.ConfusingTernary"}) + @SuppressWarnings({ + "PMD.CyclomaticComplexity", + "PMD.CognitiveComplexity", + "PMD.NPathComplexity", + "PMD.ConfusingTernary" + }) public static String getMethodDefinitionForInterface( final @NotNull Method method, final String defaultMethodDescription @@ -269,6 +274,29 @@ public static List getMethodParametersTypes(final @NotNull Method method return types; } + /** + * Get main type for the specified parameter (?int -> main type is int). + * + * @param parameter Parameter + * + * @return String + */ + public static String getMainType(final @NotNull Parameter parameter) { + final List types = extractMultipleTypesFromString( + parameter.getDeclaredType().toString() + ); + + for (final String type : types) { + if (PhpLangUtil.isFqn(type)) { + return type; + } else if (PhpLangUtil.isParameterTypeHint(type, parameter.getProject())) { + return type; + } + } + + return null; + } + /** * Get method return type. * @@ -309,6 +337,7 @@ public static String getMethodReturnType(final @NotNull Method method) { * * @return List[String] */ + @SuppressWarnings("PMD.CognitiveComplexity") public static List getMethodExceptionsTypes(final @NotNull Method method) { final List types = new ArrayList<>();