diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index 2eee35173..cb127831f 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -68,6 +68,7 @@ + @@ -214,6 +215,8 @@ + + diff --git a/resources/fileTemplates/internal/Magento Data Model Interface.php.ft b/resources/fileTemplates/internal/Magento Data Model Interface.php.ft new file mode 100644 index 000000000..78fde3427 --- /dev/null +++ b/resources/fileTemplates/internal/Magento Data Model Interface.php.ft @@ -0,0 +1,42 @@ +getData(self::$propertyUpperSnake); + } + + /** + * @inheritDoc + */ + public function set$propertyUpperCamel($$propertyLowerCamel) + { + return $this->setData(self::$propertyUpperSnake, $$propertyLowerCamel); + } + #end + #end +} diff --git a/resources/fileTemplates/internal/Magento Data Model.php.html b/resources/fileTemplates/internal/Magento Data Model.php.html new file mode 100644 index 000000000..e69de29bb diff --git a/resources/magento2/validation.properties b/resources/magento2/validation.properties index 77afecd47..d836d2655 100644 --- a/resources/magento2/validation.properties +++ b/resources/magento2/validation.properties @@ -29,3 +29,4 @@ validator.mustNotBeEmptyShouldContainLettersOrNumbers=Must not be empty, should validator.magentoRouteIdInvalid=The route id is invalid validator.magentoAclResourceIdInvalid=The ACL resource id is invalid validator.lowercaseCharacters={0} must contain lowercase characters only +validator.lowerSnakeCase=The {0} field must be of the lower snake case format diff --git a/src/com/magento/idea/magento2plugin/actions/generation/NewDataModelAction.java b/src/com/magento/idea/magento2plugin/actions/generation/NewDataModelAction.java new file mode 100644 index 000000000..e15ff1551 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/NewDataModelAction.java @@ -0,0 +1,57 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.actions.generation; + +import com.intellij.ide.IdeView; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDirectory; +import com.magento.idea.magento2plugin.MagentoIcons; +import com.magento.idea.magento2plugin.actions.generation.dialog.NewDataModelDialog; +import org.jetbrains.annotations.NotNull; + +public class NewDataModelAction extends AnAction { + public static final String ACTION_NAME = "Magento 2 Data Model"; + public static final String ACTION_DESCRIPTION = "Create a new Magento 2 Data Model"; + + /** + * Constructor. + */ + public NewDataModelAction() { + super(ACTION_NAME, ACTION_DESCRIPTION, MagentoIcons.MODULE); + } + + @Override + public void actionPerformed(@NotNull final AnActionEvent event) { + final DataContext dataContext = event.getDataContext(); + + final IdeView view = LangDataKeys.IDE_VIEW.getData(dataContext); + if (view == null) { + return; + } + + final Project project = CommonDataKeys.PROJECT.getData(dataContext); + if (project == null) { + return; + } + + final PsiDirectory directory = view.getOrChooseDirectory(); + if (directory == null) { + return; + } + + NewDataModelDialog.open(project, directory); + } + + @Override + public boolean isDumbAware() { + return false; + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/data/DataModelData.java b/src/com/magento/idea/magento2plugin/actions/generation/data/DataModelData.java new file mode 100644 index 000000000..30a288057 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/data/DataModelData.java @@ -0,0 +1,58 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.actions.generation.data; + +public class DataModelData { + private final String namespace; + private final String name; + private final String moduleName; + private final String fqn; + private final String interfaceFQN; + private final String properties; + + /** + * Constructor. + */ + public DataModelData( + final String namespace, + final String name, + final String moduleName, + final String fqn, + final String interfaceFQN, + final String properties + ) { + this.namespace = namespace; + this.name = name; + this.moduleName = moduleName; + this.fqn = fqn; + this.interfaceFQN = interfaceFQN; + this.properties = properties; + } + + public String getNamespace() { + return namespace; + } + + public String getName() { + return name; + } + + public String getModuleName() { + return moduleName; + } + + public String getFQN() { + return fqn; + } + + public String getInterfaceFQN() { + return interfaceFQN; + } + + public String getProperties() { + return properties; + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/data/DataModelInterfaceData.java b/src/com/magento/idea/magento2plugin/actions/generation/data/DataModelInterfaceData.java new file mode 100644 index 000000000..8b7e269b5 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/data/DataModelInterfaceData.java @@ -0,0 +1,51 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.actions.generation.data; + +public class DataModelInterfaceData { + private final String namespace; + private final String name; + private final String moduleName; + private final String fqn; + private final String properties; + + /** + * Constructor. + */ + public DataModelInterfaceData( + final String namespace, + final String name, + final String moduleName, + final String fqn, + final String properties + ) { + this.namespace = namespace; + this.name = name; + this.moduleName = moduleName; + this.fqn = fqn; + this.properties = properties; + } + + public String getNamespace() { + return namespace; + } + + public String getName() { + return name; + } + + public String getModuleName() { + return moduleName; + } + + public String getFQN() { + return fqn; + } + + public String getProperties() { + return properties; + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/data/code/ClassPropertyData.java b/src/com/magento/idea/magento2plugin/actions/generation/data/code/ClassPropertyData.java new file mode 100644 index 000000000..9c703dada --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/data/code/ClassPropertyData.java @@ -0,0 +1,35 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.actions.generation.data.code; + +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang.StringUtils; + +public class ClassPropertyData { + private final List data = new ArrayList<>(); + + /** + * Constructor. + */ + public ClassPropertyData( + final String type, + final String lowerCamelName, + final String upperCamelName, + final String lowerSnakeName, + final String upperSnakeName + ) { + data.add(upperSnakeName); + data.add(lowerSnakeName); + data.add(type); + data.add(upperCamelName); + data.add(lowerCamelName); + } + + public String string() { + return StringUtils.join(data, ";"); + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewDataModelDialog.form b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewDataModelDialog.form new file mode 100644 index 000000000..fc1d36c99 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewDataModelDialog.form @@ -0,0 +1,132 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewDataModelDialog.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewDataModelDialog.java new file mode 100644 index 000000000..07296ae35 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewDataModelDialog.java @@ -0,0 +1,325 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.actions.generation.dialog; + +import com.google.common.base.CaseFormat; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.ComboBoxTableRenderer; +import com.intellij.psi.PsiDirectory; +import com.magento.idea.magento2plugin.actions.generation.NewDataModelAction; +import com.magento.idea.magento2plugin.actions.generation.OverrideClassByAPreferenceAction; +import com.magento.idea.magento2plugin.actions.generation.data.DataModelData; +import com.magento.idea.magento2plugin.actions.generation.data.DataModelInterfaceData; +import com.magento.idea.magento2plugin.actions.generation.data.PreferenceDiXmFileData; +import com.magento.idea.magento2plugin.actions.generation.data.code.ClassPropertyData; +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.NotEmptyRule; +import com.magento.idea.magento2plugin.actions.generation.dialog.validator.rule.PhpClassRule; +import com.magento.idea.magento2plugin.actions.generation.generator.DataModelGenerator; +import com.magento.idea.magento2plugin.actions.generation.generator.DataModelInterfaceGenerator; +import com.magento.idea.magento2plugin.actions.generation.generator.PreferenceDiXmlGenerator; +import com.magento.idea.magento2plugin.actions.generation.generator.util.NamespaceBuilder; +import com.magento.idea.magento2plugin.bundles.CommonBundle; +import com.magento.idea.magento2plugin.bundles.ValidatorBundle; +import com.magento.idea.magento2plugin.magento.files.DataModel; +import com.magento.idea.magento2plugin.magento.files.DataModelInterface; +import com.magento.idea.magento2plugin.ui.table.ComboBoxEditor; +import com.magento.idea.magento2plugin.ui.table.DeleteRowButton; +import com.magento.idea.magento2plugin.ui.table.TableButton; +import com.magento.idea.magento2plugin.util.GetPhpClassByFQN; +import com.magento.idea.magento2plugin.util.RegExUtil; +import com.magento.idea.magento2plugin.util.magento.GetModuleNameByDirectoryUtil; +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.List; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableColumn; +import org.apache.commons.lang.StringUtils; + +@SuppressWarnings({ + "PMD.ExcessiveImports", + "PMD.TooManyMethods", +}) +public class NewDataModelDialog extends AbstractDialog { + private final Project project; + private final String moduleName; + private final ValidatorBundle validatorBundle; + private final CommonBundle commonBundle; + private final List properties; + private NamespaceBuilder interfaceNamespace; + private NamespaceBuilder modelNamespace; + + private static final String MODEL_NAME = "Model Name"; + private static final String PROPERTY_NAME = "Name"; + private static final String PROPERTY_TYPE = "Type"; + private static final String PROPERTY_ACTION = "Action"; + private static final String PROPERTY_DELETE = "Delete"; + + private static final String[] PROPERTY_TYPES = {"int", "float", "string", "bool"}; + + private JPanel contentPanel; + private JButton buttonOK; + private JButton buttonCancel; + private JTable propertyTable; + private JButton addProperty; + + @FieldValidation(rule = RuleRegistry.NOT_EMPTY, + message = {NotEmptyRule.MESSAGE, MODEL_NAME}) + @FieldValidation(rule = RuleRegistry.PHP_CLASS, + message = {PhpClassRule.MESSAGE, MODEL_NAME}) + private JTextField modelName; + + /** + * Constructor. + */ + public NewDataModelDialog(final Project project, final PsiDirectory directory) { + super(); + + this.project = project; + this.moduleName = GetModuleNameByDirectoryUtil.execute(directory, project); + this.validatorBundle = new ValidatorBundle(); + this.commonBundle = new CommonBundle(); + this.properties = new ArrayList<>(); + + setContentPane(contentPanel); + setModal(true); + setTitle(NewDataModelAction.ACTION_DESCRIPTION); + getRootPane().setDefaultButton(buttonOK); + + buttonOK.addActionListener((final ActionEvent event) -> onOK()); + buttonCancel.addActionListener((final ActionEvent event) -> onCancel()); + + // call onCancel() on dialog close + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(final WindowEvent event) { + onCancel(); + } + }); + + initPropertiesTable(); + + // call onCancel() on ESCAPE KEY press + contentPanel.registerKeyboardAction( + (final ActionEvent event) -> onCancel(), + KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT + ); + } + + /** + * Opens the dialog window. + */ + public static void open(final Project project, final PsiDirectory directory) { + final NewDataModelDialog dialog = new NewDataModelDialog(project, directory); + dialog.pack(); + dialog.centerDialog(dialog); + dialog.setVisible(true); + } + + private void onOK() { + if (validateFormFields()) { + buildNamespaces(); + formatProperties(); + generateModelInterfaceFile(); + generateModelFile(); + generatePreference(); + this.setVisible(false); + } + } + + @Override + protected boolean validateFormFields() { + boolean valid = false; + if (super.validateFormFields()) { + valid = true; + final String errorTitle = commonBundle.message("common.error"); + final int column = 0; + for (int row = 0; row < propertyTable.getRowCount(); row++) { + final String propertyName = ((String) propertyTable.getValueAt(row, column)).trim(); + if (propertyName.isEmpty()) { + valid = false; + final String errorMessage = validatorBundle.message( + "validator.notEmpty", "name" + ); + JOptionPane.showMessageDialog( + null, + errorMessage, + errorTitle, + JOptionPane.ERROR_MESSAGE + ); + break; + } else if (!propertyName.matches(RegExUtil.LOWER_SNAKE_CASE)) { + valid = false; + final String errorMessage = validatorBundle.message( + "validator.lowerSnakeCase", "name" + ); + JOptionPane.showMessageDialog( + null, + errorMessage, + errorTitle, + JOptionPane.ERROR_MESSAGE + ); + break; + } + } + } + + return valid; + } + + @Override + public void onCancel() { + dispose(); + } + + private void generateModelInterfaceFile() { + new DataModelInterfaceGenerator(project, new DataModelInterfaceData( + getInterfaceNamespace(), + getInterfaceName(), + getModuleName(), + getInterfaceFQN(), + getProperties() + )).generate(NewDataModelAction.ACTION_NAME, true); + } + + private void generateModelFile() { + new DataModelGenerator(project, new DataModelData( + getModelNamespace(), + getModelName(), + getModuleName(), + getModelFQN(), + getInterfaceFQN(), + getProperties() + )).generate(NewDataModelAction.ACTION_NAME, true); + } + + private void generatePreference() { + new PreferenceDiXmlGenerator(new PreferenceDiXmFileData( + getModuleName(), + GetPhpClassByFQN.getInstance(project).execute(getInterfaceFQN()), + getModelFQN(), + getModelNamespace(), + "base" + ), project).generate(OverrideClassByAPreferenceAction.ACTION_NAME); + } + + private void buildNamespaces() { + interfaceNamespace = new NamespaceBuilder( + getModuleName(), getInterfaceName(), DataModelInterface.DIRECTORY + ); + modelNamespace = new NamespaceBuilder( + getModuleName(), getModelName(), DataModel.DIRECTORY + ); + } + + /** + * Formats properties into an array of ClassPropertyData objects. + */ + private void formatProperties() { + final DefaultTableModel propertiesTable = getPropertiesTable(); + final int rowCount = propertiesTable.getRowCount(); + String name; + String type; + + for (int index = 0; index < rowCount; index++) { + name = propertiesTable.getValueAt(index, 0).toString(); + type = propertiesTable.getValueAt(index, 1).toString(); + properties.add(new ClassPropertyData(// NOPMD + type, + CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name), + CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name), + name, + CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, name) + ).string()); + } + } + + private String getModuleName() { + return moduleName; + } + + private String getInterfaceNamespace() { + return interfaceNamespace.getNamespace(); + } + + private String getInterfaceName() { + return modelName.getText().trim().concat("Interface"); + } + + private String getInterfaceFQN() { + return interfaceNamespace.getClassFqn(); + } + + private String getModelNamespace() { + return modelNamespace.getNamespace(); + } + + private String getModelName() { + return modelName.getText().trim(); + } + + private String getModelFQN() { + return modelNamespace.getClassFqn(); + } + + /** + * Gets properties as a string, ready for templating. + * "UPPER_SNAKE;lower_snake;type;UpperCamel;lowerCamel". + */ + private String getProperties() { + return StringUtils.join(properties, ","); + } + + private void initPropertiesTable() { + final DefaultTableModel propertiesTable = getPropertiesTable(); + propertiesTable.setDataVector( + new Object[][]{}, + new Object[]{ + PROPERTY_NAME, + PROPERTY_TYPE, + PROPERTY_ACTION + } + ); + + final TableColumn column = propertyTable.getColumn(PROPERTY_ACTION); + column.setCellRenderer(new TableButton(PROPERTY_DELETE)); + column.setCellEditor(new DeleteRowButton(new JCheckBox())); + + addProperty.addActionListener(e -> { + propertiesTable.addRow(new Object[]{ + "", + PROPERTY_TYPES[0], + PROPERTY_DELETE + }); + }); + + initPropertyTypeColumn(); + } + + private void initPropertyTypeColumn() { + final TableColumn formElementTypeColumn = propertyTable.getColumn(PROPERTY_TYPE); + formElementTypeColumn.setCellEditor(new ComboBoxEditor(PROPERTY_TYPES)); + formElementTypeColumn.setCellRenderer(new ComboBoxTableRenderer<>(PROPERTY_TYPES)); + } + + private DefaultTableModel getPropertiesTable() { + return (DefaultTableModel) propertyTable.getModel(); + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/DataModelGenerator.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/DataModelGenerator.java new file mode 100644 index 000000000..a80e43d01 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/DataModelGenerator.java @@ -0,0 +1,141 @@ +/* + * 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.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.DataModelData; +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.PhpClassGeneratorUtil; +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.DataModel; +import com.magento.idea.magento2plugin.util.GetFirstClassOfFile; +import com.magento.idea.magento2plugin.util.GetPhpClassByFQN; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; +import javax.swing.JOptionPane; + +public class DataModelGenerator extends FileGenerator { + private final Project project; + private final DataModelData modelData; + private final DirectoryGenerator directoryGenerator; + private final FileFromTemplateGenerator fileFromTemplateGenerator; + private final GetFirstClassOfFile getFirstClassOfFile; + private final ValidatorBundle validatorBundle; + private final CommonBundle commonBundle; + + /** + * Constructor. + */ + public DataModelGenerator(final Project project, final DataModelData modelData) { + super(project); + + this.project = project; + this.modelData = modelData; + this.directoryGenerator = DirectoryGenerator.getInstance(); + this.fileFromTemplateGenerator = FileFromTemplateGenerator.getInstance(project); + this.getFirstClassOfFile = GetFirstClassOfFile.getInstance(); + this.validatorBundle = new ValidatorBundle(); + this.commonBundle = new CommonBundle(); + } + + @Override + public PsiFile generate(final String actionName) { + final PsiFile[] files = new PsiFile[1]; + + WriteCommandAction.runWriteCommandAction(project, () -> { + PhpClass model = GetPhpClassByFQN.getInstance(project).execute( + modelData.getFQN() + ); + + if (model == null) { + model = createModel(actionName); + + if (model == null) { + final String errorMessage = this.validatorBundle.message( + "validator.file.cantBeCreated", + "Data Model" + ); + JOptionPane.showMessageDialog( + null, + errorMessage, + commonBundle.message("common.error"), + JOptionPane.ERROR_MESSAGE + ); + } else { + files[0] = model.getContainingFile(); + } + } else { + final String errorMessage = this.validatorBundle.message( + "validator.file.alreadyExists", + "Data Model" + ); + JOptionPane.showMessageDialog( + null, + errorMessage, + commonBundle.message("common.error"), + JOptionPane.ERROR_MESSAGE + ); + } + }); + + return files[0]; + } + + @Override + protected void fillAttributes(final Properties attributes) { + final List uses = getUses(); + attributes.setProperty("NAMESPACE", modelData.getNamespace()); + attributes.setProperty("USES", PhpClassGeneratorUtil.formatUses(uses)); + attributes.setProperty("NAME", modelData.getName()); + attributes.setProperty( + "EXTENDS", + PhpClassGeneratorUtil.getNameFromFqn(DataModel.DATA_OBJECT) + ); + attributes.setProperty( + "IMPLEMENTS", + PhpClassGeneratorUtil.getNameFromFqn(modelData.getInterfaceFQN()) + ); + attributes.setProperty("PROPERTIES", modelData.getProperties()); + } + + private List getUses() { + return Arrays.asList( + DataModel.DATA_OBJECT, + modelData.getInterfaceFQN() + ); + } + + private PhpClass createModel(final String actionName) { + PsiDirectory parentDirectory = ModuleIndex.getInstance(project) + .getModuleDirectoryByModuleName(modelData.getModuleName()); + final PsiFile interfaceFile; + final Properties attributes = getAttributes(); + + for (final String directory: DataModel.DIRECTORY.split("/")) { + parentDirectory = directoryGenerator.findOrCreateSubdirectory( + parentDirectory, directory + ); + } + + interfaceFile = fileFromTemplateGenerator.generate( + new DataModel(modelData.getName()), + attributes, + parentDirectory, + actionName + ); + + return interfaceFile == null ? null : getFirstClassOfFile.execute((PhpFile) interfaceFile); + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/DataModelInterfaceGenerator.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/DataModelInterfaceGenerator.java new file mode 100644 index 000000000..65865d23f --- /dev/null +++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/DataModelInterfaceGenerator.java @@ -0,0 +1,124 @@ +/* + * 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.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.DataModelInterfaceData; +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.bundles.CommonBundle; +import com.magento.idea.magento2plugin.bundles.ValidatorBundle; +import com.magento.idea.magento2plugin.indexes.ModuleIndex; +import com.magento.idea.magento2plugin.magento.files.DataModelInterface; +import com.magento.idea.magento2plugin.util.GetFirstClassOfFile; +import com.magento.idea.magento2plugin.util.GetPhpClassByFQN; +import java.util.Properties; +import javax.swing.JOptionPane; + +public class DataModelInterfaceGenerator extends FileGenerator { + private final Project project; + private final DataModelInterfaceData interfaceData; + private final DirectoryGenerator directoryGenerator; + private final FileFromTemplateGenerator fileFromTemplateGenerator; + private final GetFirstClassOfFile getFirstClassOfFile; + private final ValidatorBundle validatorBundle; + private final CommonBundle commonBundle; + + /** + * Constructor. + */ + public DataModelInterfaceGenerator( + final Project project, + final DataModelInterfaceData interfaceData + ) { + super(project); + + this.project = project; + this.interfaceData = interfaceData; + this.directoryGenerator = DirectoryGenerator.getInstance(); + this.fileFromTemplateGenerator = FileFromTemplateGenerator.getInstance(project); + this.getFirstClassOfFile = GetFirstClassOfFile.getInstance(); + this.validatorBundle = new ValidatorBundle(); + this.commonBundle = new CommonBundle(); + } + + @Override + public PsiFile generate(final String actionName) { + final PsiFile[] files = new PsiFile[1]; + + WriteCommandAction.runWriteCommandAction(project, () -> { + PhpClass modelInterface = GetPhpClassByFQN.getInstance(project).execute( + interfaceData.getFQN() + ); + + if (modelInterface == null) { + modelInterface = createInterface(actionName); + + if (modelInterface == null) { + final String errorMessage = this.validatorBundle.message( + "validator.file.cantBeCreated", + "Data Model Interface" + ); + JOptionPane.showMessageDialog( + null, + errorMessage, + commonBundle.message("common.error"), + JOptionPane.ERROR_MESSAGE + ); + } else { + files[0] = modelInterface.getContainingFile(); + } + } else { + final String errorMessage = this.validatorBundle.message( + "validator.file.alreadyExists", + "Data Model Interface" + ); + JOptionPane.showMessageDialog( + null, + errorMessage, + commonBundle.message("common.error"), + JOptionPane.ERROR_MESSAGE + ); + } + }); + + return files[0]; + } + + @Override + protected void fillAttributes(final Properties attributes) { + attributes.setProperty("NAME", interfaceData.getName()); + attributes.setProperty("NAMESPACE", interfaceData.getNamespace()); + attributes.setProperty("PROPERTIES", interfaceData.getProperties()); + } + + private PhpClass createInterface(final String actionName) { + PsiDirectory parentDirectory = ModuleIndex.getInstance(project) + .getModuleDirectoryByModuleName(interfaceData.getModuleName()); + final PsiFile interfaceFile; + final Properties attributes = getAttributes(); + + for (final String directory: DataModelInterface.DIRECTORY.split("/")) { + parentDirectory = directoryGenerator.findOrCreateSubdirectory( + parentDirectory, directory + ); + } + + interfaceFile = fileFromTemplateGenerator.generate( + new DataModelInterface(interfaceData.getName()), + attributes, + parentDirectory, + actionName + ); + + return interfaceFile == null ? null : getFirstClassOfFile.execute((PhpFile) interfaceFile); + } +} diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/util/PhpClassGeneratorUtil.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/util/PhpClassGeneratorUtil.java index 2ad8ab5ed..40677f142 100644 --- a/src/com/magento/idea/magento2plugin/actions/generation/generator/util/PhpClassGeneratorUtil.java +++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/util/PhpClassGeneratorUtil.java @@ -13,7 +13,7 @@ public final class PhpClassGeneratorUtil { private PhpClassGeneratorUtil() {} /** - * Format PHP class uses. + * Formats PHP class uses. * * @param uses List * @return String @@ -25,7 +25,7 @@ public static String formatUses(final List uses) { } /** - * Fetches class name from a fully qualified name. + * Fetches the class name from a fully qualified name. * * @param fqn FQN * @return String diff --git a/src/com/magento/idea/magento2plugin/magento/files/DataModel.java b/src/com/magento/idea/magento2plugin/magento/files/DataModel.java new file mode 100644 index 000000000..5158f55f4 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/magento/files/DataModel.java @@ -0,0 +1,34 @@ +/* + * 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 DataModel implements ModuleFileInterface { + public static final String DIRECTORY = "Model/Data"; + public static final String DATA_OBJECT = "Magento\\Framework\\DataObject"; + private final String className; + + public DataModel(final String className) { + this.className = className.concat(".php"); + } + + @Override + public String getFileName() { + return className; + } + + @Override + public String getTemplate() { + return "Magento Data Model"; + } + + @Override + public Language getLanguage() { + return PhpLanguage.INSTANCE; + } +} diff --git a/src/com/magento/idea/magento2plugin/magento/files/DataModelInterface.java b/src/com/magento/idea/magento2plugin/magento/files/DataModelInterface.java new file mode 100644 index 000000000..8b79cda6d --- /dev/null +++ b/src/com/magento/idea/magento2plugin/magento/files/DataModelInterface.java @@ -0,0 +1,33 @@ +/* + * 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 DataModelInterface implements ModuleFileInterface { + public static final String DIRECTORY = "Api/Data"; + private final String className; + + public DataModelInterface(final String className) { + this.className = className.concat(".php"); + } + + @Override + public String getFileName() { + return className; + } + + @Override + public String getTemplate() { + return "Magento Data Model Interface"; + } + + @Override + public Language getLanguage() { + return PhpLanguage.INSTANCE; + } +} diff --git a/src/com/magento/idea/magento2plugin/util/RegExUtil.java b/src/com/magento/idea/magento2plugin/util/RegExUtil.java index 66a1e7036..2ca243d6a 100644 --- a/src/com/magento/idea/magento2plugin/util/RegExUtil.java +++ b/src/com/magento/idea/magento2plugin/util/RegExUtil.java @@ -22,6 +22,9 @@ public class RegExUtil { public static final String IDENTIFIER = "[a-zA-Z0-9_\\-]*"; + public static final String LOWER_SNAKE_CASE + = "[a-z][a-z0-9_]*"; + public static final String CLI_COMMAND_NAME = "[a-zA-Z0-9_\\-\\:]*"; diff --git a/testData/actions/generation/generator/DataModelGenerator/generateDataModel/Sample.php b/testData/actions/generation/generator/DataModelGenerator/generateDataModel/Sample.php new file mode 100644 index 000000000..5d677058b --- /dev/null +++ b/testData/actions/generation/generator/DataModelGenerator/generateDataModel/Sample.php @@ -0,0 +1,25 @@ +getData(self::SAMPLE_PROPERTY); + } + + /** + * @inheritDoc + */ + public function setSampleProperty($sampleProperty) + { + return $this->setData(self::SAMPLE_PROPERTY, $sampleProperty); + } +} diff --git a/testData/actions/generation/generator/DataModelInterfaceGenerator/generateDataModelInterface/SampleInterface.php b/testData/actions/generation/generator/DataModelInterfaceGenerator/generateDataModelInterface/SampleInterface.php new file mode 100644 index 000000000..03ca695c1 --- /dev/null +++ b/testData/actions/generation/generator/DataModelInterfaceGenerator/generateDataModelInterface/SampleInterface.php @@ -0,0 +1,22 @@ +