feat(config): persistent directories, auto-open files, and strict project scope
- Implemented persistent directory memory for Action Model and Dataset generators (per project). - Added configuration to automatically open generated files in the editor with a customizable limit. - Enforced strict project-scope file selection for i18n and XSD settings. - Switched to relative path storage for i18n/XSD configuration to enhance project portability. - Improved File Browser logic to start at the current directory or fallback to project root. - Fixed compilation errors and optimized imports in configuration classes. - Bumped plugin version to 3.2.4.
This commit is contained in:
Binary file not shown.
@@ -1 +1 @@
|
||||
2026-04-10
|
||||
2026-04-18
|
||||
6
CheckAPI.java
Normal file
6
CheckAPI.java
Normal file
@@ -0,0 +1,6 @@
|
||||
import com.intellij.javaee.ExternalResourceManagerEx;
|
||||
import com.intellij.javaee.ExternalResourceManager;
|
||||
public class CheckAPI {
|
||||
public static void main(String[] args) {
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,10 @@
|
||||
<FIELD NAME="PROJ_FROM_YEAR" TYPE="NUMBER" WIDTH="4" LABEL="plcp.from_year"/>
|
||||
</FIELDS>
|
||||
</DATASET>
|
||||
<FOREIGN-DATASETS>
|
||||
<DATASET DATASET-ID="DS-PROJECT-STTGYS" MASTER-FIELDS="PROJ_ID,ACM_CODE" DETAIL-FIELDS="PROJ_ID" DELETE-WITH-MASTER="Y"/>
|
||||
<DATASET DATASET-ID="DS-PROJECT-STTGYS-ITEMS" MASTER-FIELDS="PROJ_ID,ACM_CODE" DETAIL-FIELDS="PROJ_ID" DELETE-WITH-MASTER="Y"/>
|
||||
</FOREIGN-DATASETS>
|
||||
</DATASETS>
|
||||
|
||||
<FORM>
|
||||
@@ -120,7 +124,7 @@
|
||||
<FIELD NAME="PROJ_YEAR" CAPTION="plcp.year" INPUTTYPE="TEXT" READONLY="Y" DATATYPE="TEXT"/>
|
||||
<FIELD NAME="VPJM_CODE" CAPTION="plcp.pjm_code" INPUTTYPE="TEXT" READONLY="Y"/>
|
||||
<FIELD NAME="PJM_CODE" CAPTION="plcp.pjm_name" INPUTTYPE="COMBOBOX" REQUIRE="Y" EDIT-READONLY="Y">
|
||||
<AJAX-OPTION URL="/api-data.jbx" DATASET="DS-PROJECT-02" VALUE-FIELD="PJM_CODE" TEXT-FIELD="PJM_NAME">
|
||||
<AJAX-OPTION URL="/api-data.jbx" DATASET="DS-PROJECT-02" VIEW-DATASET="DS-PROJECT-03" VALUE-FIELD="PJM_CODE" TEXT-FIELD="PJM_NAME">
|
||||
<UPDATE-FIELDS>
|
||||
<FIELD SRC="PJM_CODE" TARGET="VPJM_CODE"></FIELD>
|
||||
</UPDATE-FIELDS>
|
||||
|
||||
9
FindAPI.java
Normal file
9
FindAPI.java
Normal file
@@ -0,0 +1,9 @@
|
||||
import com.intellij.javaee.ExternalResourceManager;
|
||||
import java.lang.reflect.Method;
|
||||
public class FindAPI {
|
||||
public static void main(String[] args) {
|
||||
for (Method m : ExternalResourceManager.class.getMethods()) {
|
||||
System.out.println(m.getName() + " " + java.util.Arrays.toString(m.getParameterTypes()));
|
||||
}
|
||||
}
|
||||
}
|
||||
17
InspectXSD.java
Normal file
17
InspectXSD.java
Normal file
@@ -0,0 +1,17 @@
|
||||
import com.intellij.javaee.ExternalResourceManager;
|
||||
import com.intellij.javaee.ExternalResourceManagerEx;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class InspectXSD {
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("Methods in ExternalResourceManager:");
|
||||
for (Method m : ExternalResourceManager.class.getMethods()) {
|
||||
System.out.println(m.getName());
|
||||
}
|
||||
System.out.println("---");
|
||||
System.out.println("Methods in ExternalResourceManagerEx:");
|
||||
for (Method m : ExternalResourceManagerEx.class.getMethods()) {
|
||||
System.out.println(m.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,10 @@ plugins {
|
||||
id("org.jetbrains.intellij.platform") version "2.7.0"
|
||||
}
|
||||
group = "com.sdk.dynform.tools"
|
||||
version = "3.2.3"
|
||||
version = "3.2.4"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
||||
intellijPlatform {
|
||||
defaultRepositories()
|
||||
}
|
||||
@@ -39,12 +38,20 @@ intellijPlatform {
|
||||
}
|
||||
|
||||
changeNotes = """
|
||||
<h2>[3.2.4]</h2>
|
||||
<ul>
|
||||
<li><strong>Persistent Generation Directories:</strong> Generator now remembers the last used directory for Action Beans and Dataset XMLs independently per project.</li>
|
||||
<li><strong>Auto-Open Generated Files:</strong> Added configuration to automatically open newly created files in the editor, with a customizable limit on the number of files.</li>
|
||||
<li><strong>Strict Project-Relative Paths:</strong> I18n and XSD configuration now strictly enforces file selection within the project root and stores paths as relative for maximum portability.</li>
|
||||
<li><strong>Smart File Browser:</strong> Improved file picker logic to automatically start at the current configured directory if it's within the project, falling back to the project root otherwise.</li>
|
||||
</ul>
|
||||
<h2>[3.2.3]</h2>
|
||||
<ul>
|
||||
<li><strong>Advanced Data Referencing:</strong> Implemented comprehensive reference and completion support for <code><FOREIGN-DATASETS></code> and <code><MASTER-DATA></code> structures.</li>
|
||||
<li><strong>Contextual Field Resolution:</strong> Enhanced field resolution logic to resolve fields from datasets specified by <code>DATASET-ID</code> and <code>DATAID</code> attributes within their respective tags.</li>
|
||||
<li><strong>Recursive Resource Scanning:</strong> Improved dataset and grid scanning to search across recursively included <code>.frml</code> files, ensuring consistent navigation and completion throughout the project.</li>
|
||||
</ul>
|
||||
|
||||
<h2>[3.2.2]</h2>
|
||||
<ul>
|
||||
<li><strong>UI/UX Improvement:</strong> Updated the I18n settings to allow selecting the message bundle XML file directly via a file browser, improving configuration usability.</li>
|
||||
|
||||
1
buildSrc.kts
Normal file
1
buildSrc.kts
Normal file
@@ -0,0 +1 @@
|
||||
println("Checking methods")
|
||||
@@ -13,9 +13,11 @@ 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.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.sdk.dynform.helper.GUtils;
|
||||
import com.sdk.dynform.tools.config.DynFormSettings;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -30,12 +32,23 @@ public class GenerateBeanAction extends AnAction {
|
||||
return;
|
||||
}
|
||||
|
||||
DynFormSettings settings = DynFormSettings.getInstance(project);
|
||||
FileChooserDescriptor descriptor = new FileChooserDescriptor(false,true,false,false,false,false);
|
||||
PathChooserDialog pathChooser = FileChooserFactory.getInstance().createPathChooser(descriptor, project, null);
|
||||
VirtualFile baseDir = GUtils.findSourceRoot(project);
|
||||
|
||||
VirtualFile initialDir = GUtils.findSourceRoot(project);
|
||||
if (settings.lastBeanPackagePath != null && !settings.lastBeanPackagePath.isEmpty()) {
|
||||
VirtualFile lastDir = com.intellij.openapi.vfs.LocalFileSystem.getInstance().findFileByPath(settings.lastBeanPackagePath);
|
||||
if (lastDir != null && lastDir.isValid()) {
|
||||
initialDir = lastDir;
|
||||
}
|
||||
}
|
||||
|
||||
pathChooser.choose(baseDir, virtualFiles -> {
|
||||
String packageName = GUtils.getSelectedPackage(project, virtualFiles.getFirst());
|
||||
pathChooser.choose(initialDir, virtualFiles -> {
|
||||
VirtualFile selectedDir = virtualFiles.getFirst();
|
||||
settings.lastBeanPackagePath = selectedDir.getPath();
|
||||
|
||||
String packageName = GUtils.getSelectedPackage(project, selectedDir);
|
||||
|
||||
ArrayList<DbTable> tables = new ArrayList<>();
|
||||
for (PsiElement psiElement : psiElements) {
|
||||
|
||||
@@ -13,9 +13,11 @@ 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.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.sdk.dynform.helper.GUtils;
|
||||
import com.sdk.dynform.tools.config.DynFormSettings;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -30,12 +32,23 @@ public class GenerateBeanActionV3 extends AnAction {
|
||||
return;
|
||||
}
|
||||
|
||||
DynFormSettings settings = DynFormSettings.getInstance(project);
|
||||
FileChooserDescriptor descriptor = new FileChooserDescriptor(false,true,false,false,false,false);
|
||||
PathChooserDialog pathChooser = FileChooserFactory.getInstance().createPathChooser(descriptor, project, null);
|
||||
VirtualFile baseDir = GUtils.findSourceRoot(project);
|
||||
|
||||
VirtualFile initialDir = GUtils.findSourceRoot(project);
|
||||
if (settings.lastBeanPackagePath != null && !settings.lastBeanPackagePath.isEmpty()) {
|
||||
VirtualFile lastDir = com.intellij.openapi.vfs.LocalFileSystem.getInstance().findFileByPath(settings.lastBeanPackagePath);
|
||||
if (lastDir != null && lastDir.isValid()) {
|
||||
initialDir = lastDir;
|
||||
}
|
||||
}
|
||||
|
||||
pathChooser.choose(baseDir, virtualFiles -> {
|
||||
String packageName = GUtils.getSelectedPackage(project, virtualFiles.getFirst());
|
||||
pathChooser.choose(initialDir, virtualFiles -> {
|
||||
VirtualFile selectedDir = virtualFiles.getFirst();
|
||||
settings.lastBeanPackagePath = selectedDir.getPath();
|
||||
|
||||
String packageName = GUtils.getSelectedPackage(project, selectedDir);
|
||||
|
||||
ArrayList<DbTable> tables = new ArrayList<>();
|
||||
for (PsiElement psiElement : psiElements) {
|
||||
|
||||
@@ -13,9 +13,11 @@ 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.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.sdk.dynform.helper.GUtils;
|
||||
import com.sdk.dynform.tools.config.DynFormSettings;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -30,15 +32,22 @@ public class GenerateDatasetAction extends AnAction {
|
||||
return;
|
||||
}
|
||||
|
||||
DynFormSettings settings = DynFormSettings.getInstance(project);
|
||||
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();
|
||||
VirtualFile initialDir = project.getBaseDir();
|
||||
if (settings.lastDatasetFolderPath != null && !settings.lastDatasetFolderPath.isEmpty()) {
|
||||
VirtualFile lastDir = com.intellij.openapi.vfs.LocalFileSystem.getInstance().findFileByPath(settings.lastDatasetFolderPath);
|
||||
if (lastDir != null && lastDir.isValid()) {
|
||||
initialDir = lastDir;
|
||||
}
|
||||
}
|
||||
|
||||
pathChooser.choose(baseDir, virtualFiles -> {
|
||||
pathChooser.choose(initialDir, virtualFiles -> {
|
||||
VirtualFile targetDir = virtualFiles.getFirst();
|
||||
settings.lastDatasetFolderPath = targetDir.getPath();
|
||||
|
||||
ArrayList<DbTable> tables = new ArrayList<>();
|
||||
for (PsiElement psiElement : psiElements) {
|
||||
|
||||
@@ -5,11 +5,13 @@ import com.intellij.database.model.DasTableKey;
|
||||
import com.intellij.database.psi.DbTable;
|
||||
import com.intellij.database.util.DasUtil;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.containers.JBIterable;
|
||||
import com.sdk.dynform.helper.GUtils;
|
||||
import com.sdk.dynform.tools.config.DynFormSettings;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateExceptionHandler;
|
||||
@@ -39,7 +41,7 @@ public class GeneratorServices {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
private void genDataModel(Template template, Map<String, Object> model, VirtualFile targetDir, String classFile, ProgressIndicator indicator) {
|
||||
private VirtualFile genDataModel(Template template, Map<String, Object> model, VirtualFile targetDir, String classFile, ProgressIndicator indicator) {
|
||||
try {
|
||||
VirtualFile outputFile = targetDir.findOrCreateChildData(this, classFile);
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
@@ -51,11 +53,20 @@ public class GeneratorServices {
|
||||
indicator.setText2("Generated " + outputFile.getName());
|
||||
GUtils.showInfo(project, "ActionModels Generator", "Generated " + outputFile.getName());
|
||||
});
|
||||
return outputFile;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void openInEditor(VirtualFile file) {
|
||||
if (file != null) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
FileEditorManager.getInstance(project).openFile(file, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void execute(@NotNull ProgressIndicator indicator) {
|
||||
// Use AtomicInteger to safely count files within the lambda expression
|
||||
AtomicInteger fileCount = new AtomicInteger(0);
|
||||
@@ -81,30 +92,43 @@ public class GeneratorServices {
|
||||
Template tmpDTO = cfg.getTemplate("actionDTO.ftl");
|
||||
Template tmpDTOExt = cfg.getTemplate("actionDTO.extend.ftl");
|
||||
|
||||
tables.forEach(table -> {
|
||||
DynFormSettings settings = DynFormSettings.getInstance(project);
|
||||
|
||||
for (int i = 0; i < tables.size(); i++) {
|
||||
DbTable table = tables.get(i);
|
||||
final boolean shouldOpen = settings.autoOpenGeneratedFiles && i < settings.openFilesLimit;
|
||||
Map<String, Object> model = createModelForTable(table);
|
||||
|
||||
String className = model.get("className").toString();
|
||||
String classFile = className + ".java";
|
||||
|
||||
genDataModel(tmpBean, model, beanDir, classFile, indicator);
|
||||
VirtualFile fBean = genDataModel(tmpBean, model, beanDir, classFile, indicator);
|
||||
fileCount.getAndIncrement();
|
||||
|
||||
VirtualFile fBeanExt = null;
|
||||
if (!Files.exists(Path.of(beanExtDir.toNioPath().toString(), classFile))) {
|
||||
genDataModel(tmpBeanExt, model, beanExtDir, classFile, indicator);
|
||||
fBeanExt = genDataModel(tmpBeanExt, model, beanExtDir, classFile, indicator);
|
||||
fileCount.getAndIncrement();
|
||||
}
|
||||
|
||||
// FIX: Use a separate variable for the DTO filename to avoid overwriting the wrong file.
|
||||
String dtoClassFile = "DTO_" + className + ".java";
|
||||
genDataModel(tmpDTO, model, DTODir, dtoClassFile, indicator);
|
||||
VirtualFile fDTO = genDataModel(tmpDTO, model, DTODir, dtoClassFile, indicator);
|
||||
fileCount.getAndIncrement();
|
||||
|
||||
VirtualFile fDTOExt = null;
|
||||
if (!Files.exists(Path.of(DTOExtDir.toNioPath().toString(), dtoClassFile))) {
|
||||
genDataModel(tmpDTOExt, model, DTOExtDir, dtoClassFile, indicator);
|
||||
fDTOExt = genDataModel(tmpDTOExt, model, DTOExtDir, dtoClassFile, indicator);
|
||||
fileCount.getAndIncrement();
|
||||
}
|
||||
});
|
||||
|
||||
if (shouldOpen) {
|
||||
openInEditor(fBean);
|
||||
openInEditor(fBeanExt);
|
||||
openInEditor(fDTO);
|
||||
openInEditor(fDTOExt);
|
||||
}
|
||||
}
|
||||
|
||||
// After the loop finishes, show a single, helpful summary notification.
|
||||
String message = String.format("Generated %d files for %d tables successfully.", fileCount.get(), tables.size());
|
||||
@@ -125,14 +149,22 @@ public class GeneratorServices {
|
||||
|
||||
Template tmpDataset = cfg.getTemplate("dataset.ftl");
|
||||
|
||||
tables.forEach(table -> {
|
||||
DynFormSettings settings = DynFormSettings.getInstance(project);
|
||||
|
||||
for (int i = 0; i < tables.size(); i++) {
|
||||
DbTable table = tables.get(i);
|
||||
final boolean shouldOpen = settings.autoOpenGeneratedFiles && i < settings.openFilesLimit;
|
||||
Map<String, Object> model = createModelForTable(table);
|
||||
String tableName = model.get("tableName").toString();
|
||||
String fileName = GUtils.capitalize(tableName) + ".xml";
|
||||
|
||||
genDataModel(tmpDataset, model, targetDir, fileName, indicator);
|
||||
VirtualFile fDataset = genDataModel(tmpDataset, model, targetDir, fileName, indicator);
|
||||
fileCount.getAndIncrement();
|
||||
});
|
||||
|
||||
if (shouldOpen) {
|
||||
openInEditor(fDataset);
|
||||
}
|
||||
}
|
||||
|
||||
String message = String.format("Generated %d dataset XML files successfully.", fileCount.get());
|
||||
GUtils.showInfo(project, "Dataset XML Generation Complete", message);
|
||||
|
||||
@@ -4,9 +4,12 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import com.intellij.openapi.ui.TextComponentAccessor;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.NlsContexts;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.sdk.dynform.tools.dynform.DynFormXsdScanner;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -14,6 +17,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
|
||||
public class DynFormConfigurable implements Configurable {
|
||||
|
||||
@@ -25,6 +29,8 @@ public class DynFormConfigurable implements Configurable {
|
||||
private TextFieldWithBrowseButton i18nMessageFileField;
|
||||
private TextFieldWithBrowseButton xsdFolderField;
|
||||
private JTextField xsdPrefixField;
|
||||
private JCheckBox autoOpenCheck;
|
||||
private JSpinner openLimitSpinner;
|
||||
|
||||
public DynFormConfigurable(@NotNull Project project) {
|
||||
this.project = project;
|
||||
@@ -87,15 +93,38 @@ public class DynFormConfigurable implements Configurable {
|
||||
i18nGbc.weightx = 1.0;
|
||||
i18nMessageFileField = new TextFieldWithBrowseButton();
|
||||
|
||||
VirtualFile projectDir = ProjectUtil.guessProjectDir(project);
|
||||
FileChooserDescriptor xmlDescriptor = new FileChooserDescriptor(true, false, false, false, false, false) {
|
||||
@Override
|
||||
public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
|
||||
return super.isFileVisible(file, showHiddenFiles) && (file.isDirectory() || "xml".equalsIgnoreCase(file.getExtension()));
|
||||
if (!super.isFileVisible(file, showHiddenFiles)) return false;
|
||||
if (projectDir == null) return true;
|
||||
return VfsUtil.isAncestor(projectDir, file, false);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void validateSelectedFiles(VirtualFile @NotNull [] files) throws Exception {
|
||||
super.validateSelectedFiles(files);
|
||||
for (VirtualFile file : files) {
|
||||
if (projectDir != null && !VfsUtil.isAncestor(projectDir, file, false)) {
|
||||
throw new Exception("File must be within the project directory.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}.withRoots(projectDir);
|
||||
|
||||
i18nMessageFileField.addBrowseFolderListener("Select Message XML File", "Select the main message bundle XML file",
|
||||
project, xmlDescriptor, TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT);
|
||||
i18nMessageFileField.addBrowseFolderListener("Select Message XML File", "Select the main message bundle XML file (within project)",
|
||||
project, xmlDescriptor, new TextComponentAccessor<>() {
|
||||
@Override
|
||||
public String getText(JTextField textField) {
|
||||
return resolvePath(textField.getText(), projectDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(JTextField textField, @NotNull String text) {
|
||||
textField.setText(relativizePath(text, projectDir));
|
||||
}
|
||||
});
|
||||
|
||||
i18nPanel.add(i18nMessageFileField, i18nGbc);
|
||||
|
||||
@@ -128,8 +157,38 @@ public class DynFormConfigurable implements Configurable {
|
||||
xsdGbc.gridx = 1;
|
||||
xsdGbc.weightx = 1.0;
|
||||
xsdFolderField = new TextFieldWithBrowseButton();
|
||||
xsdFolderField.addBrowseFolderListener("Select XSD Folder", "Select the folder containing DynForm .xsd schemas",
|
||||
project, FileChooserDescriptorFactory.createSingleFolderDescriptor(), TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT);
|
||||
|
||||
FileChooserDescriptor folderDescriptor = new FileChooserDescriptor(false, true, false, false, false, false) {
|
||||
@Override
|
||||
public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
|
||||
if (!super.isFileVisible(file, showHiddenFiles)) return false;
|
||||
if (projectDir == null) return true;
|
||||
return VfsUtil.isAncestor(projectDir, file, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateSelectedFiles(VirtualFile @NotNull [] files) throws Exception {
|
||||
super.validateSelectedFiles(files);
|
||||
for (VirtualFile file : files) {
|
||||
if (projectDir != null && !VfsUtil.isAncestor(projectDir, file, false)) {
|
||||
throw new Exception("Folder must be within the project directory.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}.withRoots(projectDir);
|
||||
|
||||
xsdFolderField.addBrowseFolderListener("Select XSD Folder", "Select the folder containing DynForm .xsd schemas (within project)",
|
||||
project, folderDescriptor, new TextComponentAccessor<>() {
|
||||
@Override
|
||||
public String getText(JTextField textField) {
|
||||
return resolvePath(textField.getText(), projectDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(JTextField textField, @NotNull String text) {
|
||||
textField.setText(relativizePath(text, projectDir));
|
||||
}
|
||||
});
|
||||
xsdPanel.add(xsdFolderField, xsdGbc);
|
||||
|
||||
xsdGbc.gridx = 0;
|
||||
@@ -142,6 +201,34 @@ public class DynFormConfigurable implements Configurable {
|
||||
|
||||
mainPanel.add(xsdPanel, gbc);
|
||||
|
||||
// --- Generator Settings Group ---
|
||||
gbc.gridy++;
|
||||
JPanel generatorPanel = new JPanel(new GridBagLayout());
|
||||
generatorPanel.setBorder(BorderFactory.createTitledBorder("Action Models Generator"));
|
||||
|
||||
GridBagConstraints genGbc = new GridBagConstraints();
|
||||
genGbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
genGbc.insets = new Insets(2, 2, 2, 2);
|
||||
genGbc.gridx = 0;
|
||||
genGbc.gridy = 0;
|
||||
genGbc.weightx = 1.0;
|
||||
genGbc.gridwidth = 2;
|
||||
|
||||
autoOpenCheck = new JCheckBox("Automatically open generated files in editor");
|
||||
generatorPanel.add(autoOpenCheck, genGbc);
|
||||
|
||||
genGbc.gridy++;
|
||||
genGbc.gridwidth = 1;
|
||||
genGbc.weightx = 0.0;
|
||||
generatorPanel.add(new JLabel("Limit of tables to open:"), genGbc);
|
||||
|
||||
genGbc.gridx = 1;
|
||||
genGbc.weightx = 1.0;
|
||||
openLimitSpinner = new JSpinner(new SpinnerNumberModel(3, 0, 100, 1));
|
||||
generatorPanel.add(openLimitSpinner, genGbc);
|
||||
|
||||
mainPanel.add(generatorPanel, gbc);
|
||||
|
||||
// Spacer
|
||||
gbc.gridy++;
|
||||
gbc.weighty = 1.0;
|
||||
@@ -153,21 +240,30 @@ public class DynFormConfigurable implements Configurable {
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
DynFormSettings settings = DynFormSettings.getInstance(project);
|
||||
VirtualFile projectDir = ProjectUtil.guessProjectDir(project);
|
||||
return settings.displayMode != getCurrentModeFromUI() ||
|
||||
settings.showIcon != showIconCheck.isSelected() ||
|
||||
!settings.i18nMessageFile.equals(i18nMessageFileField.getText()) ||
|
||||
!settings.xsdFolderPath.equals(xsdFolderField.getText()) ||
|
||||
!settings.xsdPrefix.equals(xsdPrefixField.getText());
|
||||
!settings.i18nMessageFile.equals(relativizePath(i18nMessageFileField.getText(), projectDir)) ||
|
||||
!settings.xsdFolderPath.equals(relativizePath(xsdFolderField.getText(), projectDir)) ||
|
||||
!settings.xsdPrefix.equals(xsdPrefixField.getText()) ||
|
||||
settings.autoOpenGeneratedFiles != autoOpenCheck.isSelected() ||
|
||||
settings.openFilesLimit != (int) openLimitSpinner.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
DynFormSettings settings = DynFormSettings.getInstance(project);
|
||||
VirtualFile projectDir = ProjectUtil.guessProjectDir(project);
|
||||
|
||||
settings.displayMode = getCurrentModeFromUI();
|
||||
settings.showIcon = showIconCheck.isSelected();
|
||||
settings.i18nMessageFile = i18nMessageFileField.getText();
|
||||
settings.xsdFolderPath = xsdFolderField.getText();
|
||||
|
||||
settings.i18nMessageFile = relativizePath(i18nMessageFileField.getText(), projectDir);
|
||||
settings.xsdFolderPath = relativizePath(xsdFolderField.getText(), projectDir);
|
||||
|
||||
settings.xsdPrefix = xsdPrefixField.getText();
|
||||
settings.autoOpenGeneratedFiles = autoOpenCheck.isSelected();
|
||||
settings.openFilesLimit = (int) openLimitSpinner.getValue();
|
||||
|
||||
// Register XSDs to IntelliJ for this project
|
||||
DynFormXsdScanner.scanAndRegister(project, settings.xsdFolderPath, settings.xsdPrefix);
|
||||
@@ -176,6 +272,45 @@ public class DynFormConfigurable implements Configurable {
|
||||
com.intellij.codeInsight.daemon.DaemonCodeAnalyzer.getInstance(project).restart();
|
||||
}
|
||||
|
||||
private String relativizePath(String path, VirtualFile projectDir) {
|
||||
if (projectDir == null || path == null || path.isEmpty()) return path;
|
||||
File file = new File(path);
|
||||
if (!file.isAbsolute()) return path; // Already relative or placeholder
|
||||
|
||||
VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file);
|
||||
if (vFile != null) {
|
||||
String relativePath = VfsUtil.getRelativePath(vFile, projectDir);
|
||||
if (relativePath != null) return relativePath;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private String resolvePath(String path, VirtualFile projectDir) {
|
||||
if (projectDir == null) return path;
|
||||
if (path == null || path.isEmpty()) return projectDir.getPath();
|
||||
|
||||
VirtualFile vFile;
|
||||
File file = new File(path);
|
||||
if (file.isAbsolute()) {
|
||||
vFile = LocalFileSystem.getInstance().findFileByIoFile(file);
|
||||
} else {
|
||||
vFile = projectDir.findFileByRelativePath(path);
|
||||
}
|
||||
|
||||
if (vFile != null && VfsUtil.isAncestor(projectDir, vFile, false)) {
|
||||
return vFile.getPath();
|
||||
}
|
||||
return projectDir.getPath();
|
||||
}
|
||||
|
||||
private boolean isWithinProject(String path, VirtualFile projectDir) {
|
||||
if (projectDir == null || path == null || path.isEmpty()) return true;
|
||||
File file = new File(path);
|
||||
String absolutePath = file.getAbsolutePath();
|
||||
String projectPath = projectDir.getPath();
|
||||
return absolutePath.startsWith(projectPath);
|
||||
}
|
||||
|
||||
private DynFormSettings.DisplayMode getCurrentModeFromUI() {
|
||||
if (foldingBtn.isSelected()) return DynFormSettings.DisplayMode.FOLDING;
|
||||
if (inlayBtn.isSelected()) return DynFormSettings.DisplayMode.INLAY_HINTS;
|
||||
@@ -185,6 +320,7 @@ public class DynFormConfigurable implements Configurable {
|
||||
@Override
|
||||
public void reset() {
|
||||
DynFormSettings settings = DynFormSettings.getInstance(project);
|
||||
|
||||
switch (settings.displayMode) {
|
||||
case FOLDING: foldingBtn.setSelected(true); break;
|
||||
case INLAY_HINTS: inlayBtn.setSelected(true); break;
|
||||
@@ -194,5 +330,7 @@ public class DynFormConfigurable implements Configurable {
|
||||
i18nMessageFileField.setText(settings.i18nMessageFile);
|
||||
xsdFolderField.setText(settings.xsdFolderPath);
|
||||
xsdPrefixField.setText(settings.xsdPrefix);
|
||||
autoOpenCheck.setSelected(settings.autoOpenGeneratedFiles);
|
||||
openLimitSpinner.setValue(settings.openFilesLimit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,12 @@ public class DynFormSettings implements PersistentStateComponent<DynFormSettings
|
||||
public String xsdFolderPath = "";
|
||||
public String xsdPrefix = "/dynf";
|
||||
|
||||
// Action Models Generator Settings
|
||||
public boolean autoOpenGeneratedFiles = true;
|
||||
public int openFilesLimit = 3;
|
||||
public String lastBeanPackagePath = "";
|
||||
public String lastDatasetFolderPath = "";
|
||||
|
||||
public static DynFormSettings getInstance(Project project) {
|
||||
return project.getService(DynFormSettings.class);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.intellij.javaee.ExternalResourceManagerEx;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
|
||||
@@ -16,6 +17,17 @@ public class DynFormXsdScanner {
|
||||
if (project == null || folderPath == null || folderPath.isEmpty()) return;
|
||||
|
||||
File dir = new File(folderPath);
|
||||
if (!dir.exists() || !dir.isDirectory()) {
|
||||
// Try relative path from project root
|
||||
VirtualFile projectDir = ProjectUtil.guessProjectDir(project);
|
||||
if (projectDir != null) {
|
||||
VirtualFile vDir = projectDir.findFileByRelativePath(folderPath);
|
||||
if (vDir != null && vDir.isDirectory()) {
|
||||
dir = new File(vDir.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dir.exists() || !dir.isDirectory()) return;
|
||||
|
||||
File[] xsdFiles = dir.listFiles((d, name) -> name.toLowerCase().endsWith(".xsd"));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.sdk.dynform.tools.i18n;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
@@ -29,10 +30,16 @@ public class I18nUtils {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Try to find the file using its absolute path
|
||||
// Try to find the file using its absolute path or relative path from project root
|
||||
VirtualFile file = LocalFileSystem.getInstance().findFileByPath(filePath);
|
||||
if (file == null) {
|
||||
VirtualFile projectDir = ProjectUtil.guessProjectDir(project);
|
||||
if (projectDir != null) {
|
||||
file = projectDir.findFileByRelativePath(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
// If not found by absolute path, fallback to searching by filename in the project
|
||||
// If not found by path, fallback to searching by filename in the project
|
||||
if (file == null) {
|
||||
String filename = new java.io.File(filePath).getName();
|
||||
Collection<VirtualFile> files = FilenameIndex.getVirtualFilesByName(filename, GlobalSearchScope.everythingScope(project));
|
||||
|
||||
@@ -34,6 +34,13 @@
|
||||
]]></description>
|
||||
|
||||
<change-notes><![CDATA[
|
||||
<h2>[3.2.4]</h2>
|
||||
<ul>
|
||||
<li><strong>Persistent Generation Directories:</strong> Generator now remembers the last used directory for Action Beans and Dataset XMLs independently per project.</li>
|
||||
<li><strong>Auto-Open Generated Files:</strong> Added configuration to automatically open newly created files in the editor, with a customizable limit on the number of files.</li>
|
||||
<li><strong>Strict Project-Relative Paths:</strong> I18n and XSD configuration now strictly enforces file selection within the project root and stores paths as relative for maximum portability.</li>
|
||||
<li><strong>Smart File Browser:</strong> Improved file picker logic to automatically start at the current configured directory if it's within the project, falling back to the project root otherwise.</li>
|
||||
</ul>
|
||||
<h2>[3.2.3]</h2>
|
||||
<ul>
|
||||
<li><strong>Advanced Data Referencing:</strong> Implemented comprehensive reference and completion support for <code><FOREIGN-DATASETS></code> and <code><MASTER-DATA></code> structures.</li>
|
||||
|
||||
Reference in New Issue
Block a user