Skip to content

Commit

Permalink
Merge pull request #1200 from anzin/1139-add-generator-to-create-obse…
Browse files Browse the repository at this point in the history
…rver-template

1139: Creating a new observer template
bohdan-harniuk authored Sep 20, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents a14c613 + 6946398 commit fae3dc2
Showing 11 changed files with 937 additions and 0 deletions.
1 change: 1 addition & 0 deletions resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
@@ -78,6 +78,7 @@
<action id="MagentoCreateWidgetFile" class="com.magento.idea.magento2plugin.actions.context.xml.NewWidgetXmlAction"/>
<action id="MagentoCreateLayoutFile" class="com.magento.idea.magento2plugin.actions.context.xml.NewLayoutXmlAction"/>
<action id="MagentoCreateReadmeFile" class="com.magento.idea.magento2plugin.actions.context.md.NewReadmeMdAction"/>
<action id="MagentoDataPatchFile" class="com.magento.idea.magento2plugin.actions.context.php.NewObserverAction"/>
<!-- Context dependent actions -->
<separator/>
<add-to-group group-id="NewGroup" anchor="before" relative-to-action="NewXml"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
#parse("PHP File Header.php")

namespace ${NAMESPACE};

use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Event\Observer;

/**
* Observes the `${EVENT_NAME}` event.
*/
class ${CLASS_NAME} implements ObserverInterface
{
/**
* Observer for ${EVENT_NAME}.
*
* @param Observer $observer
*
* @return void
*/
public function execute(Observer $observer)
{
$event = $observer->getEvent();
// TODO: Implement observer method.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!--
/*
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<html>
<body>
<table width="100%" border="0" cellpadding="5" cellspacing="0" style="border-collapse: collapse">
<tr>
<td>
<font face="verdana" size="-1">Observers are a certain type of Magento class that can influence
general behavior, performance, or change business logic. Observers are executed whenever the
event they are configured to watch is dispatched by the event manager.
<a href="https://devdocs.magento.com/guides/v2.3/extension-dev-guide/events-and-observers.html">
Read more</a> about observers.
</font>
</td>
</tr>
<tr>
<td>
<font face="verdana" size="-1">Check out the
<a href="https://devdocs.magento.com/guides/v2.3/ext-best-practices/extension-coding/observers-bp.html">
Observers best practices</a> to have a clean and professional implementation.</font>
</td>
</tr>
</table>
<table width="100%" border="0" cellpadding="5" cellspacing="0" style="border-collapse: collapse">
<tr>
<td colspan="3"><font face="verdana" size="-1">Predefined variables explanation:</font></td>
</tr>
<tr>
<td valign="top"><nobr><font face="verdana" size="-2"><b>${NAMESPACE}</b></font></nobr></td>
<td width="10">&nbsp;</td>
<td width="100%" valign="top"><font face="verdana" size="-1">Namespace for the class.</font></td>
</tr>
<tr>
<td valign="top"><nobr><font face="verdana" size="-2"><b>${CLASS_NAME}</b></font></nobr></td>
<td width="10">&nbsp;</td>
<td width="100%" valign="top"><font face="verdana" size="-1">Class name.</font></td>
</tr>
<tr>
<td valign="top"><nobr><font face="verdana" size="-2"><b>${EVENT_NAME}</b></font></nobr></td>
<td width="10">&nbsp;</td>
<td width="100%" valign="top"><font face="verdana" size="-1">The name of the observer for the event definition.</font></td>
</tr>
</table>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

package com.magento.idea.magento2plugin.actions.context.php;

import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiFile;
import com.magento.idea.magento2plugin.actions.context.CustomGeneratorContextAction;
import com.magento.idea.magento2plugin.actions.generation.dialog.NewObserverDialog;
import com.magento.idea.magento2plugin.magento.packages.ComponentType;
import com.magento.idea.magento2plugin.magento.packages.Package;
import com.magento.idea.magento2plugin.util.magento.GetMagentoModuleUtil;
import org.jetbrains.annotations.NotNull;

public class NewObserverAction extends CustomGeneratorContextAction {

public static final String ACTION_NAME = "Magento 2 Observer";
public static final String ACTION_DESCRIPTION = "Create a new Magento 2 Observer";
public static final String ROOT_DIRECTORY = "Observer";

public NewObserverAction() {
super(ACTION_NAME, ACTION_DESCRIPTION);
}

@Override
public void actionPerformed(final @NotNull AnActionEvent event) {
final GetMagentoModuleUtil.MagentoModuleData moduleData = getModuleData();

if (event.getProject() == null || moduleData == null || getDirectory() == null) {
return;
}
final String[] templateData = moduleData.getName().split(Package.vendorModuleNameSeparator);

if (templateData.length != 2) { //NOPMD
return;
}

NewObserverDialog.open(
event.getProject(),
getDirectory(),
templateData[0],
templateData[1]
);
}

@Override
protected boolean isVisible(
final @NotNull GetMagentoModuleUtil.MagentoModuleData moduleData,
final PsiDirectory targetDirectory,
final PsiFile targetFile
) {
if (!moduleData.getType().equals(ComponentType.module)
|| !moduleData.getModuleDir().equals(targetDirectory.getParentDirectory())) {
return false;
}

return ROOT_DIRECTORY.equals(targetDirectory.getName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

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

import com.intellij.psi.PsiDirectory;
import org.jetbrains.annotations.NotNull;

public class ModuleObserverData {

private final String packageName;
private final String moduleName;
private final String classFqn;
private final String evenName;
private final PsiDirectory baseDir;
private final String className;

/**
* Constructor.
*
* @param packageName String
* @param moduleName String
* @param classFqn String
* @param evenName String
* @param baseDir PsiDirectory
* @param className PsiDirectory
*/
public ModuleObserverData(
final @NotNull String packageName,
final @NotNull String moduleName,
final @NotNull String classFqn,
final @NotNull String evenName,
final @NotNull PsiDirectory baseDir,
final @NotNull String className
) {
this.packageName = packageName;
this.moduleName = moduleName;
this.classFqn = classFqn;
this.evenName = evenName;
this.baseDir = baseDir;
this.className = className;
}

public @NotNull String getPackageName() {
return packageName;
}

public @NotNull String getModuleName() {
return moduleName;
}

public @NotNull String getClassFqn() {
return classFqn;
}

public @NotNull PsiDirectory getBaseDir() {
return baseDir;
}

public @NotNull String getClassName() {
return className;
}

public @NotNull String getEventName() {
return evenName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.magento.idea.magento2plugin.actions.generation.dialog.NewObserverDialog">
<grid id="1871d" binding="contentPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="10" left="10" bottom="10" right="10"/>
<constraints>
<xy x="71" y="78" width="648" height="361"/>
</constraints>
<properties>
<opaque value="true"/>
<preferredSize width="420" height="280"/>
<requestFocusEnabled value="true"/>
</properties>
<border type="none"/>
<children>
<grid id="9ad5" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<hspacer id="4aa6d">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<grid id="f892c" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="true" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="1b860" class="javax.swing.JButton" binding="buttonCancel">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="magento2/common" key="common.cancel"/>
</properties>
</component>
<component id="20801" class="javax.swing.JButton" binding="buttonOK">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="magento2/common" key="common.ok"/>
</properties>
</component>
</children>
</grid>
</children>
</grid>
<grid id="ad0b1" layout-manager="GridLayoutManager" row-count="8" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<toolTipText value=""/>
</properties>
<border type="none"/>
<children>
<component id="96659" class="javax.swing.JLabel" binding="directoryStructureLabel">
<constraints>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Directory Path"/>
</properties>
</component>
<component id="58ad9" class="javax.swing.JLabel" binding="evenNamesLabel">
<constraints>
<grid row="7" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Observer Even Name"/>
</properties>
</component>
<component id="be834" class="javax.swing.JLabel" binding="directoryStructureErrorMessage">
<constraints>
<grid row="5" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
<component id="f2094" class="javax.swing.JTextField" binding="directoryStructure">
<constraints>
<grid row="4" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties>
<editable value="true"/>
<horizontalAlignment value="2"/>
<text value=""/>
<toolTipText value=""/>
</properties>
</component>
<component id="12752" class="com.magento.idea.magento2plugin.ui.FilteredComboBox" binding="eventName" custom-create="true">
<constraints>
<grid row="7" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="e9631" class="javax.swing.JLabel" binding="targetAreaLabel">
<constraints>
<grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Target Area"/>
</properties>
</component>
<component id="ec3f4" class="javax.swing.JComboBox" binding="observerArea" custom-create="true">
<constraints>
<grid row="6" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="292c" class="javax.swing.JTextField" binding="className">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="ffc6e" class="javax.swing.JTextField" binding="observerName">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="2c31c" class="javax.swing.JLabel" binding="observerNameLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Observer Name"/>
</properties>
</component>
<component id="662ad" class="javax.swing.JLabel" binding="classNameLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Class Name"/>
</properties>
</component>
<component id="5ad39" class="javax.swing.JLabel" binding="observerNameErrorMessage">
<constraints>
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
<component id="d30e2" class="javax.swing.JLabel" binding="classNameErrorMessage">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
</children>
</grid>
<vspacer id="ec33f">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
</children>
</grid>
</form>
Original file line number Diff line number Diff line change
@@ -0,0 +1,355 @@
/*
* 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.ui.ComboBox;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.DocumentAdapter;
import com.intellij.util.indexing.FileBasedIndex;
import com.magento.idea.magento2plugin.actions.context.php.NewObserverAction;
import com.magento.idea.magento2plugin.actions.generation.ModuleObserverData;
import com.magento.idea.magento2plugin.actions.generation.data.ObserverEventsXmlData;
import com.magento.idea.magento2plugin.actions.generation.data.ui.ComboBoxItemData;
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.DirectoryRule;
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.IdentifierWithColonRule;
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.NotEmptyRule;
import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.PhpClassRule;
import com.magento.idea.magento2plugin.actions.generation.generator.ModuleObserverGenerator;
import com.magento.idea.magento2plugin.actions.generation.generator.ObserverEventsXmlGenerator;
import com.magento.idea.magento2plugin.actions.generation.generator.util.DirectoryGenerator;
import com.magento.idea.magento2plugin.lang.roots.MagentoTestSourceFilter;
import com.magento.idea.magento2plugin.magento.files.ModuleObserverFile;
import com.magento.idea.magento2plugin.magento.packages.Areas;
import com.magento.idea.magento2plugin.magento.packages.Package;
import com.magento.idea.magento2plugin.stubs.indexes.EventNameIndex;
import com.magento.idea.magento2plugin.ui.FilteredComboBox;
import com.magento.idea.magento2plugin.util.CamelCaseToSnakeCase;
import java.awt.event.ActionEvent;
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 java.util.Locale;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.event.DocumentEvent;
import org.jetbrains.annotations.NotNull;

@SuppressWarnings({
"PMD.TooManyFields",
"PMD.ExcessiveImports",
"PMD.AvoidInstantiatingObjectsInLoops",
"PMD.ReturnEmptyCollectionRatherThanNull"
})
public class NewObserverDialog extends AbstractDialog {

private static final String OBSERVER_NAME = "Observer Name";
private static final String CLASS_NAME = "Class Name";
private final Project project;
private final PsiDirectory baseDir;
private final String moduleName;
private final String modulePackage;
private JPanel contentPanel;
private JButton buttonOK;
private JButton buttonCancel;

@FieldValidation(rule = RuleRegistry.NOT_EMPTY,
message = {NotEmptyRule.MESSAGE, OBSERVER_NAME})
@FieldValidation(rule = RuleRegistry.IDENTIFIER_WITH_COLON,
message = {IdentifierWithColonRule.MESSAGE, OBSERVER_NAME})
private JTextField observerName;
@FieldValidation(rule = RuleRegistry.NOT_EMPTY,
message = {NotEmptyRule.MESSAGE, CLASS_NAME})
@FieldValidation(rule = RuleRegistry.PHP_CLASS,
message = {PhpClassRule.MESSAGE, CLASS_NAME})
private JTextField className;
private JTextField directoryStructure;
private FilteredComboBox eventName;
private JComboBox<ComboBoxItemData> observerArea;
private JLabel observerNameLabel;// NOPMD
private JLabel observerNameErrorMessage;// NOPMD
private JLabel classNameLabel;// NOPMD
private JLabel classNameErrorMessage;// NOPMD
private JLabel directoryStructureLabel;// NOPMD
private JLabel directoryStructureErrorMessage;// NOPMD
private JLabel evenNamesLabel;// NOPMD
private JLabel targetAreaLabel;// NOPMD

/**
* Constructor.
*
* @param project Project
* @param directory PsiDirectory
* @param modulePackage String
* @param moduleName String
*/
public NewObserverDialog(
final Project project,
final PsiDirectory directory,
final String modulePackage,
final String moduleName
) {
super();

this.project = project;
this.baseDir = directory;
this.modulePackage = modulePackage;
this.moduleName = moduleName;

setContentPane(contentPanel);
setModal(true);
setTitle(NewObserverAction.ACTION_DESCRIPTION);
getRootPane().setDefaultButton(buttonOK);

buttonOK.addActionListener((final ActionEvent event) -> onOK());
buttonCancel.addActionListener((final ActionEvent 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
contentPanel.registerKeyboardAction(
(final ActionEvent event) -> onCancel(),
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
);

className.getDocument().addDocumentListener(new DocumentAdapter() {
@SuppressWarnings("PMD.AccessorMethodGeneration")
@Override
public void textChanged(final @NotNull DocumentEvent event) {
autoCompleteObserverName();
}
});

addComponentListener(
new FocusOnAFieldListener(() -> className.requestFocusInWindow())
);
}

/**
* Open dialog.
*
* @param project Project
* @param directory PsiDirectory
*/
public static void open(
final Project project,
final PsiDirectory directory,
final String modulePackage,
final String moduleName
) {
final NewObserverDialog dialog = new NewObserverDialog(
project,
directory,
modulePackage,
moduleName
);
dialog.pack();
dialog.centerDialog(dialog);
dialog.setVisible(true);
}

private String getModuleName() {
return modulePackage.concat(Package.fqnSeparator).concat(moduleName);
}

public String getObserverName() {
return observerName.getText().trim();
}

public String getClassName() {
return className.getText().trim();
}

public String getEvenName() {
return eventName.getSelectedItem().toString();
}

public String getObserverArea() {
return this.observerArea.getSelectedItem().toString();
}

public String getDirectoryStructure() {
return directoryStructure.getText().trim();
}

protected void onOK() {
if (validateFields()) {
PsiDirectory observerDirectory = baseDir;

if (!getDirectoryStructure().isEmpty()) {
observerDirectory = DirectoryGenerator.getInstance().findOrCreateSubdirectories(
baseDir,
getDirectoryStructure()
);
}
new ModuleObserverGenerator(
new ModuleObserverData(
modulePackage,
moduleName,
getObserverClassFqn(),
getEvenName(),
observerDirectory,
ModuleObserverFile.resolveClassNameFromInput(getClassName())
),
project
).generate(NewObserverAction.ACTION_NAME, true);

new ObserverEventsXmlGenerator(
new ObserverEventsXmlData(
getObserverArea(),
getModuleName().replace(
Package.fqnSeparator,
Package.vendorModuleNameSeparator
),
getEvenName(),
getObserverName(),
getObserverClassFqn().concat(Package.fqnSeparator).concat(
ModuleObserverFile.resolveClassNameFromInput(getClassName())
)
),
project
).generate(NewObserverAction.ACTION_NAME);
exit();
}
}

private boolean validateFields() {
final PsiFile[] directoryFiles = getDirectoryFiles(baseDir);

if (directoryFiles != null) {
for (final PsiFile file : directoryFiles) {
final String className = ModuleObserverFile.resolveClassNameFromInput(
getClassName()
);
if (file.getName().equals(className + ModuleObserverFile.EXTENSION)) {
showErrorMessage(
fieldsValidationsList.get(1).getField(),
"Class name " + className + " already exist."
);

return false;
}
}
}
if (!getDirectoryStructure().isEmpty()
&& !DirectoryRule.getInstance().check(getDirectoryStructure())
) {
showErrorMessage(
this.getClass().getDeclaredFields()[11],
"The Directory Path field does not contain a valid directory."
);

return false;
}

return validateFormFields();
}

private PsiFile[] getDirectoryFiles(final PsiDirectory targetDirectory) {
PsiDirectory directory = targetDirectory;

if (!getDirectoryStructure().isEmpty()) {
final String[] directories = getDirectoryStructure().split(Package.V_FILE_SEPARATOR);

for (final String dir : directories) {
final PsiDirectory subdirectory = directory.findSubdirectory(dir);

if (subdirectory == null) {
return null;
}

directory = subdirectory;
}
}

return directory.getFiles();
}

private String getObserverClassFqn() {
final String folderStructureFqn = getDirectoryStructure().replace(
Package.V_FILE_SEPARATOR, Package.fqnSeparator
);
String folderFqn = NewObserverAction.ROOT_DIRECTORY;

if (!folderStructureFqn.isEmpty()) {
folderFqn = folderFqn.concat(Package.fqnSeparator).concat(folderStructureFqn);
}

return getModuleName().concat(Package.fqnSeparator).concat(folderFqn);
}

private void autoCompleteObserverName() {
final String className = getClassName();

if (className.isEmpty()) {
return;
}
final String modifiedClassName = ModuleObserverFile.resolveClassNameFromInput(className);
final String classNameInSnakeCase = CamelCaseToSnakeCase.getInstance()
.convert(modifiedClassName);

final String modulePackageModified = modulePackage.substring(0, 1)
.toLowerCase(Locale.getDefault()) + modulePackage.substring(1);
final String moduleNameModified = moduleName.substring(0, 1)
.toLowerCase(Locale.getDefault()) + moduleName.substring(1);

observerName.setText(
modulePackageModified + "_" + moduleNameModified + "_" + classNameInSnakeCase
);
}

@SuppressWarnings({"PMD.UnusedPrivateMethod"})
private void createUIComponents() {
observerArea = new ComboBox<>();

for (final Areas areaEntry : Areas.values()) {
observerArea.addItem(new ComboBoxItemData(areaEntry.toString(), areaEntry.toString()));
}
final Collection<String> events = FileBasedIndex.getInstance().getAllKeys(
EventNameIndex.KEY, project
);
// Filter all events declared only for tests.
events.removeIf(event -> {
final Collection<VirtualFile> files = FileBasedIndex.getInstance()
.getContainingFiles(
EventNameIndex.KEY,
event,
GlobalSearchScope.allScope(project)
);
final List<VirtualFile> realObservers = new ArrayList<>();

for (final VirtualFile file : files) {
if (!MagentoTestSourceFilter.isTestSources(file, project)) {
realObservers.add(file);
}
}

return realObservers.isEmpty();
});
this.eventName = new FilteredComboBox(new ArrayList<>(events));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* 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.PsiFile;
import com.magento.idea.magento2plugin.actions.generation.ModuleObserverData;
import com.magento.idea.magento2plugin.actions.generation.generator.util.FileFromTemplateGenerator;
import com.magento.idea.magento2plugin.magento.files.ModuleObserverFile;
import java.util.Properties;
import org.jetbrains.annotations.NotNull;

public class ModuleObserverGenerator extends FileGenerator {

private final ModuleObserverData moduleObserverData;
private final FileFromTemplateGenerator fileFromTemplateGenerator;

/**
* Construct generator.
*
* @param moduleObserverData ModuleObserverData
* @param project Project
*/
public ModuleObserverGenerator(
final @NotNull ModuleObserverData moduleObserverData,
final Project project
) {
super(project);
this.moduleObserverData = moduleObserverData;
this.fileFromTemplateGenerator = new FileFromTemplateGenerator(project);
}

/**
* Generate file.
*
* @param actionName String
*
* @return PsiFile
*/
@Override
public PsiFile generate(final String actionName) {
return fileFromTemplateGenerator.generate(
new ModuleObserverFile(moduleObserverData.getClassName()),
getAttributes(),
moduleObserverData.getBaseDir(),
actionName
);
}

/**
* Fill template properties.
*
* @param attributes Properties
*/
@Override
protected void fillAttributes(final Properties attributes) {
attributes.setProperty("CLASS_NAME", ModuleObserverFile.resolveClassNameFromInput(
moduleObserverData.getClassName()
));
attributes.setProperty("NAMESPACE", moduleObserverData.getClassFqn());
attributes.setProperty("EVENT_NAME", moduleObserverData.getEventName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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;
import org.jetbrains.annotations.NotNull;

public class ModuleObserverFile implements ModuleFileInterface {

public static final String FILE_SUFFIX = "Observer";
public static final String EXTENSION = ".php";
public static final String TEMPLATE = "Magento Module Observer File";
private final String className;

public ModuleObserverFile(
final @NotNull String className
) {
this.className = resolveClassNameFromInput(className);
}

/**
* Resolve class name from user input.
*
* @param input String
*
* @return String
*/
public static String resolveClassNameFromInput(final String input) {
if (input.length() <= FILE_SUFFIX.length()) {
return input + FILE_SUFFIX;
}
final String suffix = input.substring(input.length() - FILE_SUFFIX.length());

if (FILE_SUFFIX.equals(suffix)) {
return input;
} else {
return input + FILE_SUFFIX;
}
}

@Override
public String getFileName() {
return className + EXTENSION;
}

@Override
public String getTemplate() {
return TEMPLATE;
}

@Override
public Language getLanguage() {
return PhpLanguage.INSTANCE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Foo\Bar\Observer;

use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Event\Observer;

/**
* Observes the `test_event_name` event.
*/
class TestClassObserver implements ObserverInterface
{
/**
* Observer for test_event_name.
*
* @param Observer $observer
*
* @return void
*/
public function execute(Observer $observer)
{
$event = $observer->getEvent();
// TODO: Implement observer method.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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.ModuleObserverData;
import com.magento.idea.magento2plugin.magento.files.ModuleObserverFile;

public final class ModuleObserverGeneratorTest extends BaseGeneratorTestCase {

private static final String CLASS_NAME = "TestClassObserver";

/**
* Test module README.md file generation.
*/
public void testGenerateModuleObserverFile() {
final PsiFile expectedFile = myFixture.configureByFile(
getFixturePath(CLASS_NAME + ModuleObserverFile.EXTENSION)
);
final ModuleObserverGenerator generator = new ModuleObserverGenerator(
new ModuleObserverData(
"Foo",
"Bar",
"Foo\\Bar\\Observer",
"test_event_name",
getProjectDirectory(),
CLASS_NAME
),
myFixture.getProject()
);
final PsiFile generatedFile = generator.generate("test");

assertGeneratedFileIsCorrect(
expectedFile,
generatedFile
);
}
}

0 comments on commit fae3dc2

Please sign in to comment.