From af8f35840c3c9a62bf07e829c71db5c0ffe644f5 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko Date: Tue, 31 Mar 2020 16:54:46 +0300 Subject: [PATCH] Added override class by a reference action --- CHANGELOG.md | 3 +- resources/META-INF/plugin.xml | 4 + .../Magento Module DI Xml Preference.xml.ft | 1 + .../Magento Module DI Xml Preference.xml.html | 10 + .../Magento Php Preference Class.php.ft | 14 ++ .../OverrideClassByAPreferenceAction.java | 76 +++++++ .../data/PreferenceDiXmFileData.java | 49 +++++ .../generation/data/PreferenceFileData.java | 63 ++++++ .../generation/dialog/AbstractDialog.java | 19 ++ .../dialog/CreateAPluginDialog.java | 11 +- .../dialog/NewMagentoModuleDialog.java | 11 +- .../OverrideClassByAPreferenceDialog.form | 157 +++++++++++++++ .../OverrideClassByAPreferenceDialog.java | 186 ++++++++++++++++++ ...rideClassByAPreferenceDialogValidator.java | 70 +++++++ .../generator/PluginDiXmlGenerator.java | 69 +------ .../generator/PreferenceClassGenerator.java | 87 ++++++++ .../generator/PreferenceDiXmlGenerator.java | 89 +++++++++ .../generator/util/FindOrCreateDiXml.java | 62 ++++++ .../generator/util/GetCodeTemplate.java | 1 + .../generator/util/XmlFilePositionUtil.java | 41 ++++ .../magento/files/ModuleDiXml.java | 8 + .../magento/files/PhpPreference.java | 42 ++++ 22 files changed, 992 insertions(+), 81 deletions(-) create mode 100644 resources/fileTemplates/code/Magento Module DI Xml Preference.xml.ft create mode 100644 resources/fileTemplates/code/Magento Module DI Xml Preference.xml.html create mode 100644 resources/fileTemplates/internal/Magento Php Preference Class.php.ft create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/OverrideClassByAPreferenceAction.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/data/PreferenceDiXmFileData.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/data/PreferenceFileData.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/dialog/AbstractDialog.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/dialog/OverrideClassByAPreferenceDialog.form create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/dialog/OverrideClassByAPreferenceDialog.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/OverrideClassByAPreferenceDialogValidator.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/generator/PreferenceClassGenerator.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/generator/PreferenceDiXmlGenerator.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/generator/util/FindOrCreateDiXml.java create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/generator/util/XmlFilePositionUtil.java create mode 100644 src/com/magento/idea/magento2plugin/magento/files/PhpPreference.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 2827a1ecc..8e3ee87d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,8 @@ * Create a New Magento 2 Module action * Code Inspection: Duplicated Observer Usage in events XML * Create a Plugin class for a class public method action - * Code Inspection: Warning regarding Cacheable false attribute in default XML + * Code Inspection: Warning regarding Cacheable false attribute in default XML + * Create a Preference for a class action 0.3.0 ============= diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index 480e048b8..79dd2ac3d 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -60,6 +60,9 @@ + + + @@ -130,6 +133,7 @@ + diff --git a/resources/fileTemplates/code/Magento Module DI Xml Preference.xml.ft b/resources/fileTemplates/code/Magento Module DI Xml Preference.xml.ft new file mode 100644 index 000000000..ca2967a62 --- /dev/null +++ b/resources/fileTemplates/code/Magento Module DI Xml Preference.xml.ft @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/fileTemplates/code/Magento Module DI Xml Preference.xml.html b/resources/fileTemplates/code/Magento Module DI Xml Preference.xml.html new file mode 100644 index 000000000..078962598 --- /dev/null +++ b/resources/fileTemplates/code/Magento Module DI Xml Preference.xml.html @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/resources/fileTemplates/internal/Magento Php Preference Class.php.ft b/resources/fileTemplates/internal/Magento Php Preference Class.php.ft new file mode 100644 index 000000000..0a7a8aefb --- /dev/null +++ b/resources/fileTemplates/internal/Magento Php Preference Class.php.ft @@ -0,0 +1,14 @@ + pair = this.findPhpClass(event); + PsiFile psiFile = pair.getFirst(); + PhpClass phpClass = pair.getSecond(); + targetClass = phpClass; + if (!(psiFile instanceof PhpFile) && phpClass != null) { + this.setStatus(event, false); + return; + } + } else { + this.setStatus(event, false); + return; + } + this.setStatus(event, true); + } + + private void setStatus(AnActionEvent event, boolean status) { + event.getPresentation().setVisible(status); + event.getPresentation().setEnabled(status); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + OverrideClassByAPreferenceDialog.open(e.getProject(), this.targetClass); + } + + @Override + public boolean isDumbAware() { + return false; + } + + private Pair findPhpClass(@NotNull AnActionEvent event) { + PsiFile psiFile = event.getData(PlatformDataKeys.PSI_FILE); + + PhpClass phpClass = null; + if (psiFile instanceof PhpFile) { + phpClass = getFirstClassOfFile.execute((PhpFile) psiFile); + } + + return Pair.create(psiFile, phpClass); + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/data/PreferenceDiXmFileData.java b/src/com/magento/idea/magento2plugin/actions/generation/data/PreferenceDiXmFileData.java new file mode 100644 index 000000000..fefadceb7 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/data/PreferenceDiXmFileData.java @@ -0,0 +1,49 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +package com.magento.idea.magento2plugin.actions.generation.data; + +import com.jetbrains.php.lang.psi.elements.PhpClass; + +public class PreferenceDiXmFileData { + private String preferenceModule; + private PhpClass targetClass; + private String preferenceFqn; + private String namespace; + private String area; + + public PreferenceDiXmFileData( + String preferenceModule, + PhpClass targetClass, + String preferenceFqn, + String namespace, + String area + ) { + this.preferenceModule = preferenceModule; + this.targetClass = targetClass; + this.preferenceFqn = preferenceFqn; + this.namespace = namespace; + this.area = area; + } + + public String getPreferenceModule() { + return preferenceModule; + } + + public PhpClass getTargetClass() { + return targetClass; + } + + public String getPreferenceFqn() { + return preferenceFqn; + } + + public String getNamespace() { + return namespace; + } + + public String getArea() { + return area; + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/data/PreferenceFileData.java b/src/com/magento/idea/magento2plugin/actions/generation/data/PreferenceFileData.java new file mode 100644 index 000000000..2ec66b27c --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/data/PreferenceFileData.java @@ -0,0 +1,63 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +package com.magento.idea.magento2plugin.actions.generation.data; + +import com.jetbrains.php.lang.psi.elements.PhpClass; + +public class PreferenceFileData { + private String preferenceDirectory; + private String preferenceClassName; + private String preferenceModule; + private PhpClass targetClass; + private String preferenceFqn; + private String namespace; + private boolean inheritClass; + + public PreferenceFileData( + String preferenceDirectory, + String preferenceClassName, + String preferenceModule, + PhpClass targetClass, + String preferenceFqn, + String namespace, + boolean inheritClass + ) { + this.preferenceDirectory = preferenceDirectory; + this.preferenceClassName = preferenceClassName; + this.preferenceModule = preferenceModule; + this.targetClass = targetClass; + this.preferenceFqn = preferenceFqn; + this.namespace = namespace; + this.inheritClass = inheritClass; + } + + public String getPreferenceClassName() { + return preferenceClassName; + } + + public String getPreferenceDirectory() { + return preferenceDirectory; + } + + public String getPreferenceModule() { + return preferenceModule; + } + + public PhpClass getTargetClass() { + return targetClass; + } + + public String getPreferenceFqn() { + return preferenceFqn; + } + + public String getNamespace() { + return namespace; + } + + public boolean isInheritClass() { + return inheritClass; + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/AbstractDialog.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/AbstractDialog.java new file mode 100644 index 000000000..f1bdb4d97 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/AbstractDialog.java @@ -0,0 +1,19 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +package com.magento.idea.magento2plugin.actions.generation.dialog; + +import javax.swing.*; +import java.awt.*; + +public abstract class AbstractDialog extends JDialog { + protected void pushToMiddle() { + Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); + this.setLocation(dim.width / 2 -this.getSize().width / 2, dim.height / 2 - this.getSize().height / 2); + } + + protected void onCancel() { + this.setVisible(false); + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/CreateAPluginDialog.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/CreateAPluginDialog.java index cfc8b135d..ea589b7d3 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/dialog/CreateAPluginDialog.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/CreateAPluginDialog.java @@ -24,7 +24,7 @@ import java.io.File; import java.util.List; -public class CreateAPluginDialog extends JDialog { +public class CreateAPluginDialog extends AbstractDialog { @NotNull private final Project project; private Method targetMethod; @@ -100,11 +100,6 @@ private void fillTargetAreaOptions() { } } - private void pushToMiddle() { - Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); - this.setLocation(dim.width / 2 -this.getSize().width / 2, dim.height / 2 - this.getSize().height / 2); - } - private void onOK() { if (!validator.validate(project)) { return; @@ -160,10 +155,6 @@ public String getPluginModule() { return this.pluginModule.getSelectedItem().toString(); } - private void onCancel() { - this.setVisible(false); - } - public static void open(@NotNull Project project, Method targetMethod, PhpClass targetClass) { CreateAPluginDialog dialog = new CreateAPluginDialog(project, targetMethod, targetClass); dialog.pack(); diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewMagentoModuleDialog.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewMagentoModuleDialog.java index eca84efa2..c2825a20b 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewMagentoModuleDialog.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewMagentoModuleDialog.java @@ -28,7 +28,7 @@ import java.awt.*; import java.awt.event.*; -public class NewMagentoModuleDialog extends JDialog { +public class NewMagentoModuleDialog extends AbstractDialog { @NotNull private final Project project; @NotNull @@ -109,11 +109,6 @@ private void detectPackageName(@NotNull PsiDirectory initialBaseDir) { } } - private void pushToMiddle() { - Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); - this.setLocation(dim.width / 2 -this.getSize().width / 2, dim.height / 2 - this.getSize().height / 2); - } - private void onOK() { if (!validator.validate()) { return; @@ -185,10 +180,6 @@ public String getModuleVersion() { return this.moduleVersion.getText().trim(); } - private void onCancel() { - this.setVisible(false); - } - public static void open(@NotNull Project project, @NotNull PsiDirectory initialBaseDir, @Nullable PsiFile file, @Nullable IdeView view, @Nullable Editor editor) { NewMagentoModuleDialog dialog = new NewMagentoModuleDialog(project, initialBaseDir, file, view, editor); dialog.pack(); diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/OverrideClassByAPreferenceDialog.form b/src/com/magento/idea/magento2plugin/actions/generation/dialog/OverrideClassByAPreferenceDialog.form new file mode 100644 index 000000000..1134c122a --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/OverrideClassByAPreferenceDialog.form @@ -0,0 +1,157 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/OverrideClassByAPreferenceDialog.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/OverrideClassByAPreferenceDialog.java new file mode 100644 index 000000000..437879e06 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/OverrideClassByAPreferenceDialog.java @@ -0,0 +1,186 @@ +/* + * 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.psi.PsiFile; +import com.intellij.util.ArrayUtil; +import com.jetbrains.php.lang.psi.elements.PhpClass; +import com.magento.idea.magento2plugin.actions.generation.OverrideClassByAPreferenceAction; +import com.magento.idea.magento2plugin.actions.generation.data.PreferenceDiXmFileData; +import com.magento.idea.magento2plugin.actions.generation.data.PreferenceFileData; +import com.magento.idea.magento2plugin.actions.generation.dialog.validator.OverrideClassByAPreferenceDialogValidator; +import com.magento.idea.magento2plugin.actions.generation.generator.PreferenceClassGenerator; +import com.magento.idea.magento2plugin.actions.generation.generator.PreferenceDiXmlGenerator; +import com.magento.idea.magento2plugin.indexes.ModuleIndex; +import com.magento.idea.magento2plugin.magento.packages.Package; +import com.magento.idea.magento2plugin.ui.FilteredComboBox; +import org.jetbrains.annotations.NotNull; +import javax.swing.*; +import java.awt.event.*; +import java.io.File; +import java.util.List; + +public class OverrideClassByAPreferenceDialog extends AbstractDialog { + @NotNull + private final Project project; + private PhpClass targetClass; + @NotNull + private final OverrideClassByAPreferenceDialogValidator validator; + private JPanel contentPane; + private JButton buttonOK; + private JButton buttonCancel; + private JTextField preferenceClassName; + private JLabel preferenceClassNameLabel; + private JTextField preferenceDirectory; + private JLabel selectPreferenceModule; + private FilteredComboBox preferenceModule; + private JComboBox preferenceArea; + private JLabel preferenceAreaLabel; + private JCheckBox inheritClass; + private JLabel inheritClassLabel; + private JLabel preferenceDirectoryLabel; + + public OverrideClassByAPreferenceDialog(@NotNull Project project, PhpClass targetClass) { + this.project = project; + this.targetClass = targetClass; + this.validator = OverrideClassByAPreferenceDialogValidator.getInstance(this); + + setContentPane(contentPane); + setModal(true); + getRootPane().setDefaultButton(buttonOK); + pushToMiddle(); + fillTargetAreaOptions(); + if (targetClass.isFinal()) { + inheritClass.setVisible(false); + inheritClassLabel.setVisible(false); + } + suggestPreferenceClassName(targetClass); + suggestPreferenceDirectory(targetClass); + + buttonOK.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onOK(); + } + }); + + buttonCancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onCancel(); + } + }); + + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + onCancel(); + } + }); + + contentPane.registerKeyboardAction(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onCancel(); + } + }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + } + + private void suggestPreferenceDirectory(PhpClass targetClass) { + String[] fqnParts = targetClass.getPresentableFQN().split("\\\\"); + if (fqnParts.length != 0) { + fqnParts = ArrayUtil.remove(fqnParts, fqnParts.length - 1); + } + if (fqnParts[1] != null) { + fqnParts = ArrayUtil.remove(fqnParts, 1); + } + if (fqnParts[0] != null) { + fqnParts = ArrayUtil.remove(fqnParts, 0); + } + String suggestedDirectory = String.join(File.separator, fqnParts); + preferenceDirectory.setText(suggestedDirectory); + } + + private void suggestPreferenceClassName(PhpClass targetClass) { + preferenceClassName.setText(targetClass.getName()); + } + + private void fillTargetAreaOptions() { + for(Package.Areas area: Package.Areas.values()) { + preferenceArea.addItem(area.toString()); + } + } + + private void onOK() { + if (!validator.validate(project)) { + return; + } + PsiFile diXml = new PreferenceDiXmlGenerator(new PreferenceDiXmFileData( + getPreferenceModule(), + targetClass, + getPreferenceClassFqn(), + getNamespace(), + getPreferenceArea() + ), project).generate(OverrideClassByAPreferenceAction.ACTION_NAME); + if (diXml == null) { + JOptionPane.showMessageDialog(null, "Preference already declared in the target module!", "Error", JOptionPane.ERROR_MESSAGE); + return; + } + + new PreferenceClassGenerator(new PreferenceFileData( + getPreferenceDirectory(), + getPreferenceClassName(), + getPreferenceModule(), + targetClass, + getPreferenceClassFqn(), + getNamespace(), + isInheritClass() + ), project).generate(OverrideClassByAPreferenceAction.ACTION_NAME, true); + + + this.setVisible(false); + } + + public String getPreferenceClassName() { + return this.preferenceClassName.getText().trim(); + } + + public String getPreferenceDirectory() { + return this.preferenceDirectory.getText().trim(); + } + + public String getPreferenceArea() { + return this.preferenceArea.getSelectedItem().toString(); + } + + public String getPreferenceModule() { + return this.preferenceModule.getSelectedItem().toString(); + } + + public boolean isInheritClass() { + return this.inheritClass.isSelected(); + } + + public static void open(@NotNull Project project, PhpClass targetClass) { + OverrideClassByAPreferenceDialog dialog = new OverrideClassByAPreferenceDialog(project, targetClass); + dialog.pack(); + dialog.setVisible(true); + } + + private void createUIComponents() { + List allModulesList = ModuleIndex.getInstance(project).getEditableModuleNames(); + + this.preferenceModule = new FilteredComboBox(allModulesList); + } + + private String getNamespace() { + String targetModule = getPreferenceModule(); + String namespace = targetModule.replace(Package.VENDOR_MODULE_NAME_SEPARATOR, Package.FQN_SEPARATOR); + namespace = namespace.concat(Package.FQN_SEPARATOR); + return namespace.concat(getPreferenceDirectory().replace(File.separator, Package.FQN_SEPARATOR)); + } + + private String getPreferenceClassFqn() { + return getNamespace().concat(Package.FQN_SEPARATOR).concat(getPreferenceClassName()); + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/OverrideClassByAPreferenceDialogValidator.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/OverrideClassByAPreferenceDialogValidator.java new file mode 100644 index 000000000..1d3a2f9e2 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/OverrideClassByAPreferenceDialogValidator.java @@ -0,0 +1,70 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +package com.magento.idea.magento2plugin.actions.generation.dialog.validator; + +import com.intellij.openapi.project.Project; +import com.magento.idea.magento2plugin.actions.generation.dialog.OverrideClassByAPreferenceDialog; +import com.magento.idea.magento2plugin.indexes.ModuleIndex; +import com.magento.idea.magento2plugin.util.Regex; +import javax.swing.*; +import java.util.List; + +public class OverrideClassByAPreferenceDialogValidator { + private static OverrideClassByAPreferenceDialogValidator INSTANCE = null; + private OverrideClassByAPreferenceDialog dialog; + + public static OverrideClassByAPreferenceDialogValidator getInstance(OverrideClassByAPreferenceDialog dialog) { + if (null == INSTANCE) { + INSTANCE = new OverrideClassByAPreferenceDialogValidator(); + } + INSTANCE.dialog = dialog; + return INSTANCE; + } + + public boolean validate(Project project) + { + String errorTitle = "Error"; + String preferenceClassName = dialog.getPreferenceClassName(); + if (preferenceClassName.length() == 0) { + JOptionPane.showMessageDialog(null, "Preference Class Name must not be empty.", errorTitle, JOptionPane.ERROR_MESSAGE); + return false; + } + + if (!preferenceClassName.matches(Regex.ALPHANUMERIC)) { + JOptionPane.showMessageDialog(null, "Preference Class Name must contain letters and numbers only.", errorTitle, JOptionPane.ERROR_MESSAGE); + return false; + } + + if (!Character.isUpperCase(preferenceClassName.charAt(0)) && !Character.isDigit(preferenceClassName.charAt(0))) { + JOptionPane.showMessageDialog(null, "Preference Class Name must start from a number or a capital letter", errorTitle, JOptionPane.ERROR_MESSAGE); + return false; + } + + String preferenceDirectory = dialog.getPreferenceDirectory(); + if (preferenceDirectory.length() == 0) { + JOptionPane.showMessageDialog(null, "Preference Directory must not be empty.", errorTitle, JOptionPane.ERROR_MESSAGE); + return false; + } + + if (!preferenceDirectory.matches(Regex.DIRECTORY)) { + JOptionPane.showMessageDialog(null, "Preference Directory is not valid.", errorTitle, JOptionPane.ERROR_MESSAGE); + return false; + } + + String preferenceModule = dialog.getPreferenceModule(); + if (preferenceModule.length() == 0) { + JOptionPane.showMessageDialog(null, "Preference Module must not be empty.", errorTitle, JOptionPane.ERROR_MESSAGE); + return false; + } + + List allModulesList = ModuleIndex.getInstance(project).getEditableModuleNames(); + if (!allModulesList.contains(preferenceModule)) { + JOptionPane.showMessageDialog(null, "No such module '".concat(preferenceModule).concat("'."), errorTitle, JOptionPane.ERROR_MESSAGE); + return false; + } + + return true; + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/PluginDiXmlGenerator.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/PluginDiXmlGenerator.java index 7376fb293..25d68fb13 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/generator/PluginDiXmlGenerator.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/PluginDiXmlGenerator.java @@ -7,32 +7,26 @@ import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.*; -import com.intellij.xml.util.XmlUtil; import com.jetbrains.php.lang.PhpLangUtil; import com.magento.idea.magento2plugin.actions.generation.data.PluginDiXmlData; -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.FindOrCreateDiXml; import com.magento.idea.magento2plugin.actions.generation.generator.util.GetCodeTemplate; -import com.magento.idea.magento2plugin.indexes.ModuleIndex; +import com.magento.idea.magento2plugin.actions.generation.generator.util.XmlFilePositionUtil; import com.magento.idea.magento2plugin.magento.files.ModuleDiXml; -import com.magento.idea.magento2plugin.magento.packages.Package; -import com.magento.idea.magento2plugin.util.magento.FileBasedIndexUtil; import com.magento.idea.magento2plugin.xml.XmlPsiTreeUtil; import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.util.*; public class PluginDiXmlGenerator extends FileGenerator { - private final FileFromTemplateGenerator fileFromTemplateGenerator; - private final DirectoryGenerator directoryGenerator; private final GetCodeTemplate getCodeTemplate; + private final FindOrCreateDiXml findOrCreateDiXml; + private final XmlFilePositionUtil positionUtil; private PluginDiXmlData pluginFileData; private Project project; private boolean isTypeDeclared; @@ -41,14 +35,14 @@ public PluginDiXmlGenerator(@NotNull PluginDiXmlData pluginFileData, Project pro super(project); this.pluginFileData = pluginFileData; this.project = project; - this.fileFromTemplateGenerator = FileFromTemplateGenerator.getInstance(project); - this.directoryGenerator = DirectoryGenerator.getInstance(); this.getCodeTemplate = GetCodeTemplate.getInstance(project); + this.findOrCreateDiXml = FindOrCreateDiXml.getInstance(project); + this.positionUtil = XmlFilePositionUtil.getInstance(); } public PsiFile generate(String actionName) { - PsiFile diXmlFile = findOrCreateDiXml(actionName); + PsiFile diXmlFile = findOrCreateDiXml.execute(actionName, pluginFileData.getPluginModule(), pluginFileData.getArea()); XmlAttributeValue typeAttributeValue = getTypeAttributeValue((XmlFile) diXmlFile); boolean isPluginDeclared = false; this.isTypeDeclared = false; @@ -69,8 +63,8 @@ public PsiFile generate(String actionName) } int insertPos = isTypeDeclared - ? getEndPositionOfTag(PsiTreeUtil.getParentOfType(typeAttributeValue, XmlTag.class)) - : getRootInsertPosition((XmlFile) diXmlFile); + ? positionUtil.getEndPositionOfTag(PsiTreeUtil.getParentOfType(typeAttributeValue, XmlTag.class)) + : positionUtil.getRootInsertPosition((XmlFile) diXmlFile); if (textBuf.length() > 0 && insertPos >= 0) { PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project); Document document = psiDocumentManager.getDocument(diXmlFile); @@ -131,49 +125,4 @@ protected void fillAttributes(Properties attributes) { attributes.setProperty("PLUGIN_NAME", pluginFileData.getPluginName()); attributes.setProperty("SORT_ORDER", pluginFileData.getSortOrder()); } - - private PsiFile findOrCreateDiXml(String actionName) { - PsiDirectory parentDirectory = ModuleIndex.getInstance(project).getModuleDirectoryByModuleName(pluginFileData.getPluginModule()); - ArrayList pluginDirectories = new ArrayList<>(); - pluginDirectories.add(Package.MODULE_BASE_AREA_DIR); - if (!getArea().equals(Package.Areas.base)) { - pluginDirectories.add(getArea().toString()); - } - for (String pluginDirectory: pluginDirectories) { - parentDirectory = directoryGenerator.findOrCreateSubdirectory(parentDirectory, pluginDirectory); - } - ModuleDiXml moduleDiXml = new ModuleDiXml(); - PsiFile diXml = FileBasedIndexUtil.findModuleConfigFile( - moduleDiXml.getFileName(), - getArea(), - pluginFileData.getPluginModule(), - project - ); - if (diXml == null) { - diXml = fileFromTemplateGenerator.generate(moduleDiXml, new Properties(), parentDirectory, actionName); - } - return diXml; - } - - public Package.Areas getArea() { - return Package.getAreaByString(pluginFileData.getArea()); - } - - private int getRootInsertPosition(XmlFile xmlFile) { - int insertPos = -1; - XmlTag rootTag = xmlFile.getRootTag(); - if (rootTag == null) { - return insertPos; - } - return getEndPositionOfTag(rootTag); - } - - private int getEndPositionOfTag(XmlTag tag) { - PsiElement tagEnd = XmlUtil.getTokenOfType(tag, XmlTokenType.XML_END_TAG_START); - if (tagEnd == null) { - return -1; - } - - return tagEnd.getTextOffset(); - } } diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/PreferenceClassGenerator.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/PreferenceClassGenerator.java new file mode 100644 index 000000000..fb5893c42 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/PreferenceClassGenerator.java @@ -0,0 +1,87 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +package com.magento.idea.magento2plugin.actions.generation.generator; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiFile; +import com.jetbrains.php.lang.psi.PhpFile; +import com.jetbrains.php.lang.psi.elements.PhpClass; +import com.magento.idea.magento2plugin.actions.generation.data.PreferenceFileData; +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.indexes.ModuleIndex; +import com.magento.idea.magento2plugin.magento.files.PhpPreference; +import com.magento.idea.magento2plugin.util.GetFirstClassOfFile; +import com.magento.idea.magento2plugin.util.GetPhpClassByFQN; +import org.jetbrains.annotations.NotNull; +import javax.swing.*; +import java.io.File; +import java.util.Properties; + +public class PreferenceClassGenerator extends FileGenerator { + private PreferenceFileData preferenceFileData; + private Project project; + private final DirectoryGenerator directoryGenerator; + private final FileFromTemplateGenerator fileFromTemplateGenerator; + private final GetFirstClassOfFile getFirstClassOfFile; + + public PreferenceClassGenerator(@NotNull PreferenceFileData preferenceFileData, Project project) { + super(project); + this.directoryGenerator = DirectoryGenerator.getInstance(); + this.fileFromTemplateGenerator = FileFromTemplateGenerator.getInstance(project); + this.getFirstClassOfFile = GetFirstClassOfFile.getInstance(); + this.preferenceFileData = preferenceFileData; + this.project = project; + } + + public PsiFile generate(String actionName) { + PhpClass pluginClass = GetPhpClassByFQN.getInstance(project).execute(preferenceFileData.getPreferenceFqn()); + if (pluginClass == null) { + pluginClass = createPluginClass(actionName); + } + if (pluginClass == null) { + JOptionPane.showMessageDialog(null, "Preference Class cant be created!", "Error", JOptionPane.ERROR_MESSAGE); + return null; + } + + return pluginClass.getContainingFile(); + } + + private PhpClass createPluginClass(String actionName) { + PsiDirectory parentDirectory = ModuleIndex.getInstance(project).getModuleDirectoryByModuleName(getPreferenceModule()); + String[] pluginDirectories = preferenceFileData.getPreferenceDirectory().split(File.separator); + for (String pluginDirectory: pluginDirectories) { + parentDirectory = directoryGenerator.findOrCreateSubdirectory(parentDirectory, pluginDirectory); + } + + Properties attributes = getAttributes(); + PsiFile pluginFile = fileFromTemplateGenerator.generate(PhpPreference.getInstance(preferenceFileData.getPreferenceClassName()), attributes, parentDirectory, actionName); + if (pluginFile == null) { + return null; + } + return getFirstClassOfFile.execute((PhpFile) pluginFile); + } + + protected void fillAttributes(Properties attributes) { + String preferenceClassName = preferenceFileData.getPreferenceClassName(); + attributes.setProperty("NAME", preferenceFileData.getPreferenceClassName()); + attributes.setProperty("NAMESPACE", preferenceFileData.getNamespace()); + if (!preferenceFileData.isInheritClass()) { + return; + } + String parentClassName = preferenceFileData.getTargetClass().getName(); + if (!parentClassName.equals(preferenceClassName)) { + attributes.setProperty("USE", preferenceFileData.getTargetClass().getPresentableFQN()); + attributes.setProperty("EXTENDS", parentClassName); + return; + } + attributes.setProperty("EXTENDS", preferenceFileData.getTargetClass().getFQN()); + } + + public String getPreferenceModule() { + return preferenceFileData.getPreferenceModule(); + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/PreferenceDiXmlGenerator.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/PreferenceDiXmlGenerator.java new file mode 100644 index 000000000..addf516a2 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/PreferenceDiXmlGenerator.java @@ -0,0 +1,89 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +package com.magento.idea.magento2plugin.actions.generation.generator; + +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.xml.*; +import com.jetbrains.php.lang.PhpLangUtil; +import com.magento.idea.magento2plugin.actions.generation.data.PreferenceDiXmFileData; +import com.magento.idea.magento2plugin.actions.generation.generator.util.FindOrCreateDiXml; +import com.magento.idea.magento2plugin.actions.generation.generator.util.GetCodeTemplate; +import com.magento.idea.magento2plugin.actions.generation.generator.util.XmlFilePositionUtil; +import com.magento.idea.magento2plugin.magento.files.ModuleDiXml; +import com.magento.idea.magento2plugin.xml.XmlPsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import java.io.IOException; +import java.util.Collection; +import java.util.Properties; + +public class PreferenceDiXmlGenerator extends FileGenerator { + private final GetCodeTemplate getCodeTemplate; + private final FindOrCreateDiXml findOrCreateDiXml; + private final XmlFilePositionUtil positionUtil; + private PreferenceDiXmFileData preferenceDiXmFileData; + private Project project; + + public PreferenceDiXmlGenerator(@NotNull PreferenceDiXmFileData preferenceDiXmFileData, Project project) { + super(project); + this.preferenceDiXmFileData = preferenceDiXmFileData; + this.project = project; + this.getCodeTemplate = GetCodeTemplate.getInstance(project); + this.findOrCreateDiXml = FindOrCreateDiXml.getInstance(project); + this.positionUtil = XmlFilePositionUtil.getInstance(); + } + + public PsiFile generate(String actionName) + { + PsiFile diXmlFile = findOrCreateDiXml.execute(actionName, preferenceDiXmFileData.getPreferenceModule(), preferenceDiXmFileData.getArea()); + boolean isPreferenceDeclared = getTypeAttributeValue((XmlFile) diXmlFile); + if (isPreferenceDeclared) { + return null; + } + WriteCommandAction.runWriteCommandAction(project, () -> { + StringBuffer textBuf = new StringBuffer(); + try { + textBuf.append(getCodeTemplate.execute(ModuleDiXml.TEMPLATE_PREFERENCE, getAttributes())); + } catch (IOException e) { + e.printStackTrace(); + return; + } + + int insertPos = positionUtil.getRootInsertPosition((XmlFile) diXmlFile); + if (textBuf.length() > 0 && insertPos >= 0) { + PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project); + Document document = psiDocumentManager.getDocument(diXmlFile); + document.insertString(insertPos, textBuf); + int endPos = insertPos + textBuf.length() + 1; + CodeStyleManager.getInstance(project).reformatText(diXmlFile, insertPos, endPos); + psiDocumentManager.commitDocument(document); + } + }); + + return diXmlFile; + } + + private boolean getTypeAttributeValue(XmlFile diXml) { + Collection preferences = XmlPsiTreeUtil.findAttributeValueElements(diXml, ModuleDiXml.PREFERENCE_TAG_NAME, ModuleDiXml.PREFERENCE_ATTR_FOR); + String pluginClassFqn = preferenceDiXmFileData.getTargetClass().getPresentableFQN(); + for (XmlAttributeValue preference: preferences) { + if (!PhpLangUtil.toPresentableFQN(preference.getValue()).equals(pluginClassFqn)) { + continue; + } + return true; + } + + return false; + } + + protected void fillAttributes(Properties attributes) { + attributes.setProperty("FOR", preferenceDiXmFileData.getTargetClass().getPresentableFQN()); + attributes.setProperty("TYPE", preferenceDiXmFileData.getPreferenceFqn()); + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/util/FindOrCreateDiXml.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/util/FindOrCreateDiXml.java new file mode 100644 index 000000000..f3c8237f8 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/util/FindOrCreateDiXml.java @@ -0,0 +1,62 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +package com.magento.idea.magento2plugin.actions.generation.generator.util; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiFile; +import com.magento.idea.magento2plugin.indexes.ModuleIndex; +import com.magento.idea.magento2plugin.magento.files.ModuleDiXml; +import com.magento.idea.magento2plugin.magento.packages.Package; +import com.magento.idea.magento2plugin.util.magento.FileBasedIndexUtil; +import java.util.ArrayList; +import java.util.Properties; + +public class FindOrCreateDiXml { + private static FindOrCreateDiXml INSTANCE = null; + private Project project; + + public static FindOrCreateDiXml getInstance(Project project) { + if (null == INSTANCE) { + INSTANCE = new FindOrCreateDiXml(project); + } + + return INSTANCE; + } + + FindOrCreateDiXml(Project project) { + this.project = project; + } + + public PsiFile execute(String actionName, String moduleName, String area) { + DirectoryGenerator directoryGenerator = DirectoryGenerator.getInstance(); + FileFromTemplateGenerator fileFromTemplateGenerator = FileFromTemplateGenerator.getInstance(project); + + PsiDirectory parentDirectory = ModuleIndex.getInstance(project).getModuleDirectoryByModuleName(moduleName); + ArrayList fileDirectories = new ArrayList<>(); + fileDirectories.add(Package.MODULE_BASE_AREA_DIR); + if (!getArea(area).equals(Package.Areas.base)) { + fileDirectories.add(getArea(area).toString()); + } + for (String fileDirectory: fileDirectories) { + parentDirectory = directoryGenerator.findOrCreateSubdirectory(parentDirectory, fileDirectory); + } + ModuleDiXml moduleDiXml = new ModuleDiXml(); + PsiFile diXml = FileBasedIndexUtil.findModuleConfigFile( + moduleDiXml.getFileName(), + getArea(area), + moduleName, + project + ); + if (diXml == null) { + diXml = fileFromTemplateGenerator.generate(moduleDiXml, new Properties(), parentDirectory, actionName); + } + return diXml; + } + + private Package.Areas getArea(String area) { + return Package.getAreaByString(area); + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/util/GetCodeTemplate.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/util/GetCodeTemplate.java index 0553e8649..cfa1983ba 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/generator/util/GetCodeTemplate.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/util/GetCodeTemplate.java @@ -21,6 +21,7 @@ public static GetCodeTemplate getInstance(Project project) { return INSTANCE; } + GetCodeTemplate (Project project) { this.project = project; } diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/util/XmlFilePositionUtil.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/util/XmlFilePositionUtil.java new file mode 100644 index 000000000..2998155d3 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/util/XmlFilePositionUtil.java @@ -0,0 +1,41 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +package com.magento.idea.magento2plugin.actions.generation.generator.util; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.psi.xml.XmlTokenType; +import com.intellij.xml.util.XmlUtil; + +public class XmlFilePositionUtil { + private static XmlFilePositionUtil INSTANCE = null; + + public static XmlFilePositionUtil getInstance() { + if (null == INSTANCE) { + INSTANCE = new XmlFilePositionUtil(); + } + + return INSTANCE; + } + + public int getRootInsertPosition(XmlFile xmlFile) { + int insertPos = -1; + XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag == null) { + return insertPos; + } + return getEndPositionOfTag(rootTag); + } + + public int getEndPositionOfTag(XmlTag tag) { + PsiElement tagEnd = XmlUtil.getTokenOfType(tag, XmlTokenType.XML_END_TAG_START); + if (tagEnd == null) { + return -1; + } + + return tagEnd.getTextOffset(); + } +} diff --git a/src/com/magento/idea/magento2plugin/magento/files/ModuleDiXml.java b/src/com/magento/idea/magento2plugin/magento/files/ModuleDiXml.java index 973993809..6dfc1c8c1 100644 --- a/src/com/magento/idea/magento2plugin/magento/files/ModuleDiXml.java +++ b/src/com/magento/idea/magento2plugin/magento/files/ModuleDiXml.java @@ -10,11 +10,19 @@ public class ModuleDiXml implements ModuleFileInterface { public static String FILE_NAME = "di.xml"; public static String TEMPLATE = "Magento Module DI Xml"; + + //code templates public static String TEMPLATE_PLUGIN = "Magento Module DI Xml Plugin"; + public static String TEMPLATE_PREFERENCE = "Magento Module DI Xml Preference"; + + //tags public static String PLUGIN_TYPE_TAG = "type"; public static String PLUGIN_TYPE_ATTRIBUTE = "type"; public static String PLUGIN_TAG_NAME = "plugin"; public static String PLUGIN_TYPE_ATTR_NAME = "name"; + public static String PREFERENCE_TAG_NAME = "preference"; + public static String PREFERENCE_ATTR_FOR = "for"; + private static ModuleDiXml INSTANCE = null; public static ModuleDiXml getInstance() { diff --git a/src/com/magento/idea/magento2plugin/magento/files/PhpPreference.java b/src/com/magento/idea/magento2plugin/magento/files/PhpPreference.java new file mode 100644 index 000000000..eade2bcd3 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/magento/files/PhpPreference.java @@ -0,0 +1,42 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +package com.magento.idea.magento2plugin.magento.files; + +import com.intellij.lang.Language; +import com.jetbrains.php.lang.PhpLanguage; + +public class PhpPreference implements ModuleFileInterface { + public static String TEMPLATE = "Magento Php Preference Class"; + + private static PhpPreference INSTANCE = null; + private String fileName; + + public static PhpPreference getInstance(String className) { + if (null == INSTANCE) { + INSTANCE = new PhpPreference(); + } + INSTANCE.setFileName(className.concat(".php")); + return INSTANCE; + } + + @Override + public String getFileName() { + return this.fileName; + } + + @Override + public String getTemplate() { + return TEMPLATE; + } + + @Override + public Language getLanguage() { + return PhpLanguage.INSTANCE; + } + + private void setFileName(String filename) { + this.fileName = filename; + } +}