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:
2026-02-27 12:33:58 +07:00
parent c40ce04f73
commit d501ecec22
12 changed files with 313 additions and 50 deletions

View File

@@ -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());
}

View File

@@ -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());
}
})
);
}
});
}
}

View File

@@ -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());
}
})
);
}
});
}
}

View File

@@ -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);
});