From ffe1712318e9bc0a3845d0bdee733d0e426747e9 Mon Sep 17 00:00:00 2001 From: skidus Date: Sat, 18 Apr 2026 12:57:07 +0700 Subject: [PATCH] feat(dynform): structural validation and generator enhancements - Implemented DynFormAnnotator to validate field usage in and against their respective definitions. - Refined ActionBean generator logic for NUMBER and DATE column width calculation. - Improved window management and focus for newly generated files using FileEditorManagerEx. - Bumped plugin version to 3.2.6. --- build.gradle.kts | 15 ++-- .../tools/actionbean/GeneratorServices.java | 15 ++-- .../tools/dynform/DynFormAnnotator.java | 79 +++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 7 ++ 4 files changed, 103 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/sdk/dynform/tools/dynform/DynFormAnnotator.java diff --git a/build.gradle.kts b/build.gradle.kts index 4a3a58e..c449662 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { id("org.jetbrains.intellij.platform") version "2.7.0" } group = "com.sdk.dynform.tools" -version = "3.2.5" +version = "3.2.6" repositories { mavenCentral() @@ -38,19 +38,18 @@ intellijPlatform { } changeNotes = """ +

[3.2.6]

+
    +
  • Structural Validation: Introduced a new Annotator to validate that all fields used in <LAYOUT> and <FILTERS> are correctly defined in their respective <FIELDS> sections.
  • +
  • Generator Logic Update: Refined the column width calculation for NUMBER and DATE types in the ActionBean generator for better XML compatibility.
  • +
  • Improved File Opening: Switched to FileEditorManagerEx for opening generated files, ensuring better window focus and management in recent IDE versions.
  • +

[3.2.5]

  • Dataset to Form Mapping: Implemented comprehensive reference and completion support for the FORM-NAME attribute in DATASET > FIELDS > FIELD.
  • Smart Field Resolution: FORM-NAME now correctly resolves to hidden fields in FORM_ENTRY > FIELDS and any named fields within FORM_ENTRY > LAYOUT.
  • Recursive Form Scanning: Enhanced field discovery to scan across all form entries in the current and recursively included .frml files.
-

[3.2.4]

-
    -
  • Persistent Generation Directories: Generator now remembers the last used directory for Action Beans and Dataset XMLs independently per project.
  • -
  • Auto-Open Generated Files: Added configuration to automatically open newly created files in the editor, with a customizable limit on the number of files.
  • -
  • Strict Project-Relative Paths: I18n and XSD configuration now strictly enforces file selection within the project root and stores paths as relative for maximum portability.
  • -
  • Smart File Browser: 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.
  • -

[3.2.3]

  • Advanced Data Referencing: Implemented comprehensive reference and completion support for <FOREIGN-DATASETS> and <MASTER-DATA> structures.
  • diff --git a/src/main/java/com/sdk/dynform/tools/actionbean/GeneratorServices.java b/src/main/java/com/sdk/dynform/tools/actionbean/GeneratorServices.java index f5f6793..1b8d299 100644 --- a/src/main/java/com/sdk/dynform/tools/actionbean/GeneratorServices.java +++ b/src/main/java/com/sdk/dynform/tools/actionbean/GeneratorServices.java @@ -5,7 +5,7 @@ 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.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; @@ -62,7 +62,9 @@ public class GeneratorServices { private void openInEditor(VirtualFile file) { if (file != null) { ApplicationManager.getApplication().invokeLater(() -> { - FileEditorManager.getInstance(project).openFile(file, true); + FileEditorManagerEx manager = FileEditorManagerEx.getInstanceEx(project); + // Using 3-parameter openFile from FileEditorManagerEx to ensure it's handled correctly + manager.openFile(file, true, true); }); } } @@ -224,16 +226,19 @@ public class GeneratorServices { colModel.put("isPk", primaryKeys.contains(colName)); colModel.put("customType", dataType); + int width = column.getDasType().toDataType().getLength(); + // XML Specific fields String xmlType = "TEXT"; if ("NUMBER".equals(dataType)) { xmlType = "NUMBER"; + width = String.valueOf(width).length(); } else if ("DATE".equals(dataType)) { xmlType = "DATE"; + width = "dd/mm/yyyy hh:mm:ss".length(); } - colModel.put("xmlType", xmlType); - int width = column.getDasType().toDataType().getLength(); + colModel.put("xmlType", xmlType); colModel.put("width", Math.max(width, 0)); // Label generation: Use field comment if available, otherwise fallback to generated key @@ -254,4 +259,4 @@ public class GeneratorServices { return model; } -} \ No newline at end of file +} diff --git a/src/main/java/com/sdk/dynform/tools/dynform/DynFormAnnotator.java b/src/main/java/com/sdk/dynform/tools/dynform/DynFormAnnotator.java new file mode 100644 index 0000000..4cbe8d6 --- /dev/null +++ b/src/main/java/com/sdk/dynform/tools/dynform/DynFormAnnotator.java @@ -0,0 +1,79 @@ +package com.sdk.dynform.tools.dynform; + +import com.intellij.lang.annotation.AnnotationHolder; +import com.intellij.lang.annotation.Annotator; +import com.intellij.lang.annotation.HighlightSeverity; +import com.intellij.psi.PsiElement; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlAttributeValue; +import com.intellij.psi.xml.XmlTag; +import org.jetbrains.annotations.NotNull; + +public class DynFormAnnotator implements Annotator { + @Override + public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { + if (!(element instanceof XmlAttributeValue attrValue)) return; + + PsiElement parent = attrValue.getParent(); + if (!(parent instanceof XmlAttribute attr)) return; + + if (!"NAME".equals(attr.getName())) return; + + PsiElement tagElem = attr.getParent(); + if (!(tagElem instanceof XmlTag tag) || !"FIELD".equals(tag.getName())) return; + + // Check if this FIELD is inside a LAYOUT tag + XmlTag layoutTag = tag.getParentTag(); + while (layoutTag != null && !"LAYOUT".equals(layoutTag.getName())) { + layoutTag = layoutTag.getParentTag(); + } + + if (layoutTag == null) return; + + // The "Owner" of the LAYOUT (e.g., FORM_ENTRY, FILTERS, GRID-EDITOR) + XmlTag ownerTag = layoutTag.getParentTag(); + if (ownerTag == null) return; + + String fieldName = attrValue.getValue(); + if (fieldName == null || fieldName.isEmpty()) return; + + // Look for the FIELDS tag that is a sibling of the current LAYOUT + XmlTag fieldsTag = ownerTag.findFirstSubTag("FIELDS"); + if (fieldsTag == null) { + holder.newAnnotation(HighlightSeverity.ERROR, "No definition found in <" + ownerTag.getName() + ">") + .range(attrValue.getTextRange()) + .create(); + return; + } + + if (!isFieldDefined(fieldsTag, fieldName)) { + holder.newAnnotation(HighlightSeverity.ERROR, "Field '" + fieldName + "' is not defined in of <" + ownerTag.getName() + ">") + .range(attrValue.getTextRange()) + .create(); + } + } + + private boolean isFieldDefined(XmlTag fieldsTag, String name) { + for (XmlTag field : fieldsTag.findSubTags("FIELD")) { + if (name.equals(field.getAttributeValue("NAME"))) { + return true; + } + } + // Also check inside SECTIONS or ROWS if any + for (XmlTag sub : fieldsTag.getSubTags()) { + if ("SECTION".equals(sub.getName()) || "ROW".equals(sub.getName())) { + if (isFieldDefined(sub, name)) return true; + } + } + return false; + } + + private static boolean hasAncestorWithName(XmlTag tag, String name) { + XmlTag current = tag.getParentTag(); + while (current != null) { + if (name.equals(current.getName())) return true; + current = current.getParentTag(); + } + return false; + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 333f669..00656df 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -34,6 +34,12 @@ ]]> [3.2.6] +
      +
    • Structural Validation: Introduced a new Annotator to validate that all fields used in <LAYOUT> and <FILTERS> are correctly defined in their respective <FIELDS> sections.
    • +
    • Generator Logic Update: Refined the column width calculation for NUMBER and DATE types in the ActionBean generator for better XML compatibility.
    • +
    • Improved File Opening: Switched to FileEditorManagerEx for opening generated files, ensuring better window focus and management in recent IDE versions.
    • +

    [3.2.5]

    • Dataset to Form Mapping: Implemented comprehensive reference and completion support for the FORM-NAME attribute in DATASET > FIELDS > FIELD.
    • @@ -188,5 +194,6 @@ +