feat: implement Dataset XML generation and Action Models V3
- Added 'Generate Dataset XML' feature to automate table definitions from database schema. - Implemented intelligent label generation (prioritizing comments) and automatic type mapping for XML. - Added 'Generate Action Models V3' supporting the new sdk.db package structure. - Updated release notes and bumped version to 2.1.0. - Registered new actions with keyboard shortcuts in plugin.xml.
This commit is contained in:
@@ -48,13 +48,13 @@ public class GenerateBeanAction extends AnAction {
|
||||
}
|
||||
|
||||
private void runGenerator(Project project,ArrayList<DbTable> tables, String packageName) {
|
||||
ProgressManager.getInstance().run(new Task.Backgroundable(project, "Generate Database Action Models ...") {
|
||||
ProgressManager.getInstance().run(new Task.Backgroundable(project, "Generate database action models ...") {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
ApplicationManager.getApplication().invokeLater(() ->
|
||||
WriteCommandAction.runWriteCommandAction(project, () -> {
|
||||
try {
|
||||
new GeneratorServices(project,tables,packageName).execute(indicator);
|
||||
new GeneratorServices(project,tables,packageName, GeneratorServices.Version.V2).execute(indicator);
|
||||
} catch (Exception ex) {
|
||||
GUtils.showError(project, "An error occurred during code generation: " + ex.getMessage());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.sdk.generators.actionmodels;
|
||||
|
||||
import com.intellij.database.psi.DbTable;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.LangDataKeys;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.fileChooser.FileChooserFactory;
|
||||
import com.intellij.openapi.fileChooser.PathChooserDialog;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.sdk.generators.GUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GenerateBeanActionV3 extends AnAction {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Project project = e.getProject();
|
||||
PsiElement[] psiElements = e.getData(LangDataKeys.PSI_ELEMENT_ARRAY);
|
||||
if (project == null || psiElements == null || psiElements.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileChooserDescriptor descriptor = new FileChooserDescriptor(false,true,false,false,false,false);
|
||||
PathChooserDialog pathChooser = FileChooserFactory.getInstance().createPathChooser(descriptor, project, null);
|
||||
VirtualFile baseDir = GUtils.findSourceRoot(project);
|
||||
|
||||
pathChooser.choose(baseDir, virtualFiles -> {
|
||||
String packageName = GUtils.getSelectedPackage(project, virtualFiles.getFirst());
|
||||
|
||||
ArrayList<DbTable> tables = new ArrayList<>();
|
||||
for (PsiElement psiElement : psiElements) {
|
||||
if (psiElement instanceof DbTable) {
|
||||
tables.add((DbTable) psiElement);
|
||||
}
|
||||
}
|
||||
runGenerator(project, tables, packageName,"V3");
|
||||
});
|
||||
}
|
||||
|
||||
private void runGenerator(Project project,ArrayList<DbTable> tables, String packageName, String version) {
|
||||
ProgressManager.getInstance().run(new Task.Backgroundable(project, "Generate database action models ...") {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
ApplicationManager.getApplication().invokeLater(() ->
|
||||
WriteCommandAction.runWriteCommandAction(project, () -> {
|
||||
try {
|
||||
new GeneratorServices(project,tables,packageName, GeneratorServices.Version.V3).execute(indicator);
|
||||
} catch (Exception ex) {
|
||||
GUtils.showError(project, "An error occurred during code generation: " + ex.getMessage());
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.sdk.generators.actionmodels;
|
||||
|
||||
import com.intellij.database.psi.DbTable;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.LangDataKeys;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.fileChooser.FileChooserFactory;
|
||||
import com.intellij.openapi.fileChooser.PathChooserDialog;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.sdk.generators.GUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GenerateDatasetAction extends AnAction {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Project project = e.getProject();
|
||||
PsiElement[] psiElements = e.getData(LangDataKeys.PSI_ELEMENT_ARRAY);
|
||||
if (project == null || psiElements == null || psiElements.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileChooserDescriptor descriptor = new FileChooserDescriptor(false, true, false, false, false, false);
|
||||
descriptor.setTitle("Select Target Directory for Dataset XML");
|
||||
PathChooserDialog pathChooser = FileChooserFactory.getInstance().createPathChooser(descriptor, project, null);
|
||||
|
||||
//VirtualFile baseDir = GUtils.findSourceRoot(project);
|
||||
VirtualFile baseDir = project.getBaseDir();
|
||||
|
||||
pathChooser.choose(baseDir, virtualFiles -> {
|
||||
VirtualFile targetDir = virtualFiles.getFirst();
|
||||
|
||||
ArrayList<DbTable> tables = new ArrayList<>();
|
||||
for (PsiElement psiElement : psiElements) {
|
||||
if (psiElement instanceof DbTable) {
|
||||
tables.add((DbTable) psiElement);
|
||||
}
|
||||
}
|
||||
runGenerator(project, tables, targetDir);
|
||||
});
|
||||
}
|
||||
|
||||
private void runGenerator(Project project, ArrayList<DbTable> tables, VirtualFile targetDir) {
|
||||
ProgressManager.getInstance().run(new Task.Backgroundable(project, "Generate Dataset XML ...") {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
ApplicationManager.getApplication().invokeLater(() ->
|
||||
WriteCommandAction.runWriteCommandAction(project, () -> {
|
||||
try {
|
||||
new GeneratorServices(project, tables, "", GeneratorServices.Version.V2).executeDataset(targetDir, indicator);
|
||||
} catch (Exception ex) {
|
||||
GUtils.showError(project, "An error occurred during code generation: " + ex.getMessage());
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -28,11 +28,15 @@ public class GeneratorServices {
|
||||
private final String basePackage;
|
||||
private final Project project;
|
||||
private final ArrayList<DbTable> tables;
|
||||
private final Version version;
|
||||
|
||||
public GeneratorServices(Project project, ArrayList<DbTable> tables, String basePackage) {
|
||||
public enum Version {V2,V3}
|
||||
|
||||
public GeneratorServices(Project project, ArrayList<DbTable> tables, String basePackage, Version version) {
|
||||
this.tables = tables;
|
||||
this.project = project;
|
||||
this.basePackage = basePackage;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
private void genDataModel(Template template, Map<String, Object> model, VirtualFile targetDir, String classFile, ProgressIndicator indicator) {
|
||||
@@ -111,6 +115,33 @@ public class GeneratorServices {
|
||||
}
|
||||
}
|
||||
|
||||
public void executeDataset(VirtualFile targetDir, @NotNull ProgressIndicator indicator) {
|
||||
AtomicInteger fileCount = new AtomicInteger(0);
|
||||
try {
|
||||
Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
|
||||
cfg.setClassForTemplateLoading(this.getClass(), "/templates");
|
||||
cfg.setDefaultEncoding("UTF-8");
|
||||
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
|
||||
|
||||
Template tmpDataset = cfg.getTemplate("dataset.ftl");
|
||||
|
||||
tables.forEach(table -> {
|
||||
Map<String, Object> model = createModelForTable(table);
|
||||
String tableName = model.get("tableName").toString();
|
||||
String fileName = GUtils.capitalize(tableName) + ".xml";
|
||||
|
||||
genDataModel(tmpDataset, model, targetDir, fileName, indicator);
|
||||
fileCount.getAndIncrement();
|
||||
});
|
||||
|
||||
String message = String.format("Generated %d dataset XML files successfully.", fileCount.get());
|
||||
GUtils.showInfo(project, "Dataset XML Generation Complete", message);
|
||||
|
||||
} catch (Exception ex) {
|
||||
GUtils.showError(project, "Generation Failed \n" + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> createModelForTable(DbTable table) {
|
||||
String tableName = table.getName().toUpperCase();
|
||||
String dbSchema = table.getParent() != null ? table.getParent().getName() : "";
|
||||
@@ -123,6 +154,16 @@ public class GeneratorServices {
|
||||
model.put("className", tableName);
|
||||
model.put("dbSchema", dbSchema);
|
||||
|
||||
if (version == Version.V2) {
|
||||
model.put("db_connector", "sdk.dbutils.*");
|
||||
model.put("db_dataset", "sdk.dbutils.*");
|
||||
model.put("db_dto", "sdk.dbutils.*");
|
||||
} else {
|
||||
model.put("db_connector", "sdk.db.connector.*");
|
||||
model.put("db_dataset", "sdk.db.dataset.*");
|
||||
model.put("db_dto", "sdk.db.dto.*");
|
||||
}
|
||||
|
||||
List<Map<String, Object>> columns = new ArrayList<>();
|
||||
Set<String> primaryKeys = new HashSet<>();
|
||||
JBIterable<? extends DasTableKey> dasKeys = DasUtil.getTableKeys(table);
|
||||
@@ -133,6 +174,15 @@ public class GeneratorServices {
|
||||
}
|
||||
});
|
||||
|
||||
String entityName = tableName.toLowerCase();
|
||||
if (entityName.endsWith("_m")) {
|
||||
entityName = entityName.substring(0, entityName.length() - 2);
|
||||
} else if (entityName.endsWith("s")) {
|
||||
entityName = entityName.substring(0, entityName.length() - 1);
|
||||
}
|
||||
|
||||
final String finalEntityName = entityName;
|
||||
|
||||
JBIterable<? extends DasColumn> dasColumns = DasUtil.getColumns(table);
|
||||
dasColumns.forEach(column -> {
|
||||
Map<String, Object> colModel = new HashMap<>();
|
||||
@@ -141,6 +191,28 @@ public class GeneratorServices {
|
||||
colModel.put("name", colName);
|
||||
colModel.put("isPk", primaryKeys.contains(colName));
|
||||
colModel.put("customType", dataType);
|
||||
|
||||
// XML Specific fields
|
||||
String xmlType = "TEXT";
|
||||
if ("NUMBER".equals(dataType)) {
|
||||
xmlType = "NUMBER";
|
||||
} else if ("DATE".equals(dataType)) {
|
||||
xmlType = "DATE";
|
||||
}
|
||||
colModel.put("xmlType", xmlType);
|
||||
|
||||
int width = column.getDasType().toDataType().getLength();
|
||||
colModel.put("width", Math.max(width, 0));
|
||||
|
||||
// Label generation: Use field comment if available, otherwise fallback to generated key
|
||||
String comment = column.getComment();
|
||||
if (comment != null && !comment.isEmpty()) {
|
||||
colModel.put("label", comment);
|
||||
} else {
|
||||
String fieldPart = colName.toLowerCase();
|
||||
colModel.put("label", fieldPart);
|
||||
}
|
||||
|
||||
columns.add(colModel);
|
||||
});
|
||||
|
||||
|
||||
@@ -11,26 +11,37 @@
|
||||
]]></description>
|
||||
|
||||
<change-notes><![CDATA[
|
||||
## [1.1.2]
|
||||
fix: Update plugin version display
|
||||
Corrected the plugin version display in the IDE.
|
||||
|
||||
## [1.1.1]
|
||||
fix: Correctly generate primary keys
|
||||
The generator was failing to identify primary key columns, resulting in generated beans without them. This has been fixed by updating the code to use the `getColumnsRef()` method, which correctly retrieves the column names for the primary key.
|
||||
|
||||
## Release Notes
|
||||
|
||||
### New Features and Enhancements
|
||||
|
||||
* **DTO Generation:** Introduced new functionality to generate Data Transfer Object (DTO) classes alongside ActionBeans. This includes new FreeMarker templates (`actionDTO.ftl`, `actionDTO.extend.ftl`) and updated logic in `GeneratorServices.java`.
|
||||
* **ActionField and DTOField Enhancements:** Implemented enhancements related to the generation of `ActionField` and `DTOField` within the generated classes.
|
||||
|
||||
### Refactoring and Improvements
|
||||
|
||||
* **Project Structure Refactoring:** The project structure has been reorganized. Generator-related classes were moved to a new package (`com.sdk.generators.actionmodels`), and template directory names were standardized to `src/main/resources/templates`.
|
||||
* **Build System Updates:** Updated `build.gradle.kts`, `plugin.xml`, and Gradle wrapper files to reflect the structural and functional enhancements.
|
||||
|
||||
<h2>[2.1.0]</h2>
|
||||
<ul>
|
||||
<li>Add: Generate Dataset XML feature to generate XML table definitions from database tables.</li>
|
||||
<li>Feature: Intelligent label generation for XML datasets, prioritizing database column comments with fallback to entity-based keys.</li>
|
||||
<li>Feature: Automatic mapping of database types to XML types (TEXT, NUMBER, DATE) and column width extraction.</li>
|
||||
<li>Shortcut: Added <code>Ctrl + Alt + J</code> for the dataset generation action.</li>
|
||||
</ul>
|
||||
<h2>[2.0.1]</h2>
|
||||
<ul>
|
||||
<li>update : Refactory class structure of sdk.dbutils -> sdk.db.xxxx</li>
|
||||
<li>Add : Generate for new class structure as V3 </li>
|
||||
</ul>
|
||||
<h2>[1.1.2]</h2>
|
||||
<ul>
|
||||
<li>fix: Handle databases that do not support user-defined types</li>
|
||||
</ul>
|
||||
<h2>[1.1.1]</h2>
|
||||
<ul>
|
||||
<li>fix: Correctly generate primary keys. The generator was failing to identify primary key columns, resulting in generated beans without them. This has been fixed by updating the code to use the `getColumnsRef()` method, which correctly retrieves the column names for the primary key.</li>
|
||||
</ul>
|
||||
<h2>[1.1.0]</h2>
|
||||
<h3>New Features and Enhancements</h3>
|
||||
<ul>
|
||||
<li><strong>DTO Generation:</strong> Introduced new functionality to generate Data Transfer Object (DTO) classes alongside ActionBeans. This includes new FreeMarker templates (<code>actionDTO.ftl</code>, <code>actionDTO.extend.ftl</code>) and updated logic in <code>GeneratorServices.java</code>.</li>
|
||||
<li><strong>ActionField and DTOField Enhancements:</strong> Implemented enhancements related to the generation of <code>ActionField</code> and <code>DTOField</code> within the generated classes.</li>
|
||||
</ul>
|
||||
<h3>Refactoring and Improvements</h3>
|
||||
<ul>
|
||||
<li><strong>Project Structure Refactoring:</strong> The project structure has been reorganized. Generator-related classes were moved to a new package (<code>com.sdk.generators.actionmodels</code>), and template directory names were standardized to <code>src/main/resources/templates</code>.</li>
|
||||
<li><strong>Build System Updates:</strong> Updated <code>build.gradle.kts</code>, <code>plugin.xml</code>, and Gradle wrapper files to reflect the structural and functional enhancements.</li>
|
||||
</ul>
|
||||
]]></change-notes>
|
||||
|
||||
<depends>com.intellij.modules.platform</depends>
|
||||
@@ -46,6 +57,21 @@
|
||||
<add-to-group group-id="DatabaseViewPopupMenu" anchor="first"/>
|
||||
<keyboard-shortcut keymap="$default" first-keystroke="ctrl alt G"/>
|
||||
</action>
|
||||
<!-- Defines an action that will appear in the context menu for project directories -->
|
||||
<action id="com.sdk.generators.actionmodels.GenerateBeanAction.v3"
|
||||
class="com.sdk.generators.actionmodels.GenerateBeanActionV3"
|
||||
text="Generate Action Models V3"
|
||||
description="Generates ActionBean classes from a database schema V3 (sdk.db.xxx).">
|
||||
<add-to-group group-id="DatabaseViewPopupMenu" anchor="after" relative-to-action="com.sdk.generators.actionmodels.GenerateBeanAction"/>
|
||||
<keyboard-shortcut keymap="$default" first-keystroke="ctrl alt H"/>
|
||||
</action>
|
||||
<action id="com.sdk.generators.actionmodels.GenerateDatasetAction"
|
||||
class="com.sdk.generators.actionmodels.GenerateDatasetAction"
|
||||
text="Generate Dataset XML"
|
||||
description="Generates Dataset XML definition from a database table.">
|
||||
<add-to-group group-id="DatabaseViewPopupMenu" anchor="after" relative-to-action="com.sdk.generators.actionmodels.GenerateBeanAction.v3"/>
|
||||
<keyboard-shortcut keymap="$default" first-keystroke="ctrl alt J"/>
|
||||
</action>
|
||||
</actions>
|
||||
|
||||
<!-- in plugin.xml, inside the <extensions defaultExtensionNs="com.intellij"> tag -->
|
||||
|
||||
@@ -5,7 +5,7 @@ package ${basePackage}.bean;
|
||||
For Table : ${tableName}
|
||||
*/
|
||||
|
||||
import sdk.dbutils.*;
|
||||
import ${db_connector};
|
||||
|
||||
public class ${tableName} extends ${basePackage}.bean.base.${tableName} {
|
||||
public ${tableName}(DBConnector connector) { //class construction
|
||||
|
||||
@@ -5,7 +5,9 @@ package ${basePackage}.bean.base;
|
||||
For Table : ${tableName}
|
||||
*/
|
||||
|
||||
import sdk.dbutils.*;
|
||||
|
||||
import ${db_connector};
|
||||
import ${db_dataset};
|
||||
import sdk.utils.*;
|
||||
import ${basePackage}.dto.*;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ package ${basePackage}.dto;
|
||||
For Table : ${tableName}
|
||||
*/
|
||||
|
||||
import sdk.dbutils.*;
|
||||
import ${db_dto};
|
||||
|
||||
public class DTO_${tableName} extends ${basePackage}.dto.base.DTO_${tableName} {
|
||||
public DTO_${tableName}(DTO dto) { //class construction
|
||||
|
||||
@@ -5,7 +5,7 @@ package ${basePackage}.dto.base;
|
||||
For Table : ${tableName}
|
||||
*/
|
||||
|
||||
import sdk.dbutils.*;
|
||||
import ${db_dto};
|
||||
import sdk.utils.*;
|
||||
|
||||
public class DTO_${className} extends DTO {
|
||||
|
||||
18
src/main/resources/templates/dataset.ftl
Normal file
18
src/main/resources/templates/dataset.ftl
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DATASET ID="DS-${tableName}">
|
||||
<SCHEMA>APP</SCHEMA>
|
||||
<TABLENAME>${tableName}</TABLENAME>
|
||||
<KEYFIELDS>${keyList}</KEYFIELDS>
|
||||
<SQL>
|
||||
<SELECT>SELECT ${fieldList?replace(",", "
|
||||
, ")}
|
||||
</SELECT>
|
||||
<FROM>FROM ${tableName}</FROM>
|
||||
<ORDER>ORDER BY ${keyList}</ORDER>
|
||||
</SQL>
|
||||
<FIELDS>
|
||||
<#list columns as column>
|
||||
<FIELD NAME="${column.name}" TYPE="${column.xmlType}" LABEL="${column.label}" WIDTH="${column.width?c}"/>
|
||||
</#list>
|
||||
</FIELDS>
|
||||
</DATASET>
|
||||
Reference in New Issue
Block a user