From 4bd5ca1c4e559996f06962f08584ed32054de9f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dinis=20Ferreira?= Date: Sat, 30 May 2026 11:40:06 +0200 Subject: [PATCH] fix: validate and quick-fix duplicate languages in CHECKCFG The CHECKCFG grammar accepts multiple `for { ... }` blocks in one configuration. Nothing validated against the same language appearing twice, so duplicate blocks were silently allowed with no defined semantics for how their configurations combine. Add an error-severity validation plus a quick-fix: - checkConfiguredLanguageUnique on CheckConfiguration reports an error on each duplicate ConfiguredLanguageValidator sharing a language name. Mirrors the existing catalog/check/parameter uniqueness validators and reuses the getDuplicates() helper. Simpler than the catalog case: getLanguage() returns a String, so no proxy check is needed. - removeDuplicateLanguageConfiguration quick-fix merges every later block's parameter and catalog configurations into the first occurrence and deletes the duplicates. Tests live in CheckCfgTest; the surrounding checkcfg.core.test validation files are migrated Xtend -> Java to add them. This revives the approach from the long-closed #104 (validation + merge quick-fix), reimplemented against the current Java validator/quick-fix code, which was renamed and migrated off Xtend since 2018. Closes #103 Co-Authored-By: Claude Opus 4.7 --- .../ddk/checkcfg/validation/CheckCfgTest.java | 17 ++++++++ .../validation/CheckCfgValidator.java | 30 +++++++++++++ .../ddk/checkcfg/validation/IssueCodes.java | 1 + .../ddk/checkcfg/validation/Messages.java | 1 + .../checkcfg/validation/messages.properties | 1 + .../ui/quickfix/CheckCfgQuickfixProvider.java | 43 +++++++++++++++++++ .../ddk/checkcfg/ui/quickfix/Messages.java | 2 + .../checkcfg/ui/quickfix/messages.properties | 2 + 8 files changed, 97 insertions(+) diff --git a/com.avaloq.tools.ddk.checkcfg.core.test/src/com/avaloq/tools/ddk/checkcfg/validation/CheckCfgTest.java b/com.avaloq.tools.ddk.checkcfg.core.test/src/com/avaloq/tools/ddk/checkcfg/validation/CheckCfgTest.java index dbad41e80..96c982960 100644 --- a/com.avaloq.tools.ddk.checkcfg.core.test/src/com/avaloq/tools/ddk/checkcfg/validation/CheckCfgTest.java +++ b/com.avaloq.tools.ddk.checkcfg.core.test/src/com/avaloq/tools/ddk/checkcfg/validation/CheckCfgTest.java @@ -58,4 +58,21 @@ public void testUnknownLanguageNotOk() throws Exception { helper.assertError(model, CheckcfgPackage.Literals.CONFIGURED_LANGUAGE_VALIDATOR, IssueCodes.UNKNOWN_LANGUAGE); } + @Test + public void testDuplicateLanguageNotOk() throws Exception { + final CheckConfiguration model = parser.parse(""" + check configuration Test + + for com.avaloq.tools.ddk.^check.TestLanguage { + + } + + for com.avaloq.tools.ddk.^check.TestLanguage { + + } + + """); + helper.assertError(model, CheckcfgPackage.Literals.CONFIGURED_LANGUAGE_VALIDATOR, IssueCodes.DUPLICATE_LANGUAGE_CONFIGURATION); + } + } diff --git a/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/CheckCfgValidator.java b/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/CheckCfgValidator.java index 61a1b61f3..f65d12c8f 100644 --- a/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/CheckCfgValidator.java +++ b/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/CheckCfgValidator.java @@ -313,6 +313,36 @@ public void checkConfiguredLanguageExists(final ConfiguredLanguageValidator vali } } + /** + * Checks that within a Check Configuration all Configured Language Validators are unique, meaning that + * the same language can only be configured in one place. + * + * @param configuration + * the configuration + */ + @Check + public void checkConfiguredLanguageUnique(final CheckConfiguration configuration) { + if (configuration.getLanguageValidatorConfigurations().size() < 2) { + return; + } + Predicate predicate = new Predicate() { + @Override + public boolean apply(final ConfiguredLanguageValidator validator) { + return validator.getLanguage() != null; + } + }; + Function function = new Function() { + @Override + public String apply(final ConfiguredLanguageValidator from) { + return from.getLanguage(); + } + }; + for (final ConfiguredLanguageValidator v : getDuplicates(predicate, function, configuration.getLanguageValidatorConfigurations())) { + error(Messages.CheckCfgJavaValidator_DUPLICATE_LANGUAGE_CONFIGURATION, v, CheckcfgPackage.Literals.CONFIGURED_LANGUAGE_VALIDATOR__LANGUAGE, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, IssueCodes.DUPLICATE_LANGUAGE_CONFIGURATION); + } + + } + /** * Checks that a Configured Check has unique Configured Parameters. * diff --git a/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/IssueCodes.java b/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/IssueCodes.java index 2a9818854..75ae4c479 100644 --- a/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/IssueCodes.java +++ b/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/IssueCodes.java @@ -26,6 +26,7 @@ public final class IssueCodes { public static final String DUPLICATE_CATALOG_CONFIGURATION = ISSUE_CODE_PREFIX + "duplicate_catalog_configuration"; public static final String DUPLICATE_CHECK_CONFIGURATION = ISSUE_CODE_PREFIX + "duplicate_check_configuration"; public static final String UNKNOWN_LANGUAGE = ISSUE_CODE_PREFIX + "unknown_language"; + public static final String DUPLICATE_LANGUAGE_CONFIGURATION = ISSUE_CODE_PREFIX + "duplicate_language_configuration"; public static final String DUPLICATE_PARAMETER_CONFIGURATION = ISSUE_CODE_PREFIX + "duplicate_parameter_configuration"; public static final String SEVERITY_NOT_ALLOWED = ISSUE_CODE_PREFIX + "severity_not_allowed"; public static final String PARAMETER_VALUE_NOT_ALLOWED = ISSUE_CODE_PREFIX + "parameter_value_not_allowed"; diff --git a/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/Messages.java b/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/Messages.java index e96bcdc20..66dfb4bb3 100644 --- a/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/Messages.java +++ b/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/Messages.java @@ -25,6 +25,7 @@ public class Messages extends NLS { public static String CheckCfgJavaValidator_CONFIGURED_PARAM_EQUALS_DEFAULT; public static String CheckCfgJavaValidator_DUPLICATE_CATALOG_CONFIGURATION; public static String CheckCfgJavaValidator_DUPLICATE_CHECK_CONFIGURATION; + public static String CheckCfgJavaValidator_DUPLICATE_LANGUAGE_CONFIGURATION; public static String CheckCfgJavaValidator_DUPLICATE_PARAMETER_CONFIGURATION; public static String CheckCfgJavaValidator_SEVERITY_NOT_ALLOWED; diff --git a/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/messages.properties b/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/messages.properties index 63001722a..1ad5b893d 100644 --- a/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/messages.properties +++ b/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/validation/messages.properties @@ -5,5 +5,6 @@ CheckCfgJavaValidator_FINAL_CHECK_NOT_CONFIGURABLE=Final checks may not be confi CheckCfgJavaValidator_CONFIGURED_PARAM_EQUALS_DEFAULT=Configured value for ''{0}'' equals default CheckCfgJavaValidator_DUPLICATE_CATALOG_CONFIGURATION=Duplicate catalog configuration CheckCfgJavaValidator_DUPLICATE_CHECK_CONFIGURATION=Duplicate check configuration +CheckCfgJavaValidator_DUPLICATE_LANGUAGE_CONFIGURATION=Duplicate language configuration CheckCfgJavaValidator_DUPLICATE_PARAMETER_CONFIGURATION=Duplicate parameter configuration CheckCfgJavaValidator_SEVERITY_NOT_ALLOWED=Configured severity is not allowed \ No newline at end of file diff --git a/com.avaloq.tools.ddk.checkcfg.ui/src/com/avaloq/tools/ddk/checkcfg/ui/quickfix/CheckCfgQuickfixProvider.java b/com.avaloq.tools.ddk.checkcfg.ui/src/com/avaloq/tools/ddk/checkcfg/ui/quickfix/CheckCfgQuickfixProvider.java index 81495357b..244eebf23 100644 --- a/com.avaloq.tools.ddk.checkcfg.ui/src/com/avaloq/tools/ddk/checkcfg/ui/quickfix/CheckCfgQuickfixProvider.java +++ b/com.avaloq.tools.ddk.checkcfg.ui/src/com/avaloq/tools/ddk/checkcfg/ui/quickfix/CheckCfgQuickfixProvider.java @@ -10,6 +10,9 @@ *******************************************************************************/ package com.avaloq.tools.ddk.checkcfg.ui.quickfix; +import java.util.ArrayList; +import java.util.List; + import org.eclipse.emf.ecore.EObject; import org.eclipse.jface.text.BadLocationException; import org.eclipse.osgi.util.NLS; @@ -26,6 +29,7 @@ import com.avaloq.tools.ddk.checkcfg.checkcfg.CheckConfiguration; import com.avaloq.tools.ddk.checkcfg.checkcfg.ConfiguredCatalog; import com.avaloq.tools.ddk.checkcfg.checkcfg.ConfiguredCheck; +import com.avaloq.tools.ddk.checkcfg.checkcfg.ConfiguredLanguageValidator; import com.avaloq.tools.ddk.checkcfg.checkcfg.SeverityKind; import com.avaloq.tools.ddk.checkcfg.validation.IssueCodes; @@ -158,4 +162,43 @@ public void apply(final EObject element, final IModificationContext context) { } }); } + + /** + * Removes duplicate language configurations by merging the contents of every later occurrence into the first one + * and deleting the duplicates. + * + * @param issue + * the issue + * @param acceptor + * the acceptor + */ + @Fix(IssueCodes.DUPLICATE_LANGUAGE_CONFIGURATION) + public void removeDuplicateLanguageConfiguration(final Issue issue, final IssueResolutionAcceptor acceptor) { + acceptor.accept(issue, Messages.CheckCfgQuickfixProvider_REMOVE_DUPLICATE_LANG_LABEL, Messages.CheckCfgQuickfixProvider_REMOVE_DUPLICATE_LANG_DESCN, null, new ISemanticModification() { + @Override + public void apply(final EObject element, final IModificationContext context) { + final CheckConfiguration configuration = EcoreUtil2.getContainerOfType(element, CheckConfiguration.class); + final String languageName = ((ConfiguredLanguageValidator) element).getLanguage(); + if (configuration == null || languageName == null) { + return; + } + ConfiguredLanguageValidator first = null; + final List duplicates = new ArrayList(); + for (final ConfiguredLanguageValidator validator : configuration.getLanguageValidatorConfigurations()) { + if (languageName.equals(validator.getLanguage())) { + if (first == null) { + first = validator; + } else { + duplicates.add(validator); + } + } + } + for (final ConfiguredLanguageValidator duplicate : duplicates) { + first.getParameterConfigurations().addAll(duplicate.getParameterConfigurations()); + first.getCatalogConfigurations().addAll(duplicate.getCatalogConfigurations()); + configuration.getLanguageValidatorConfigurations().remove(duplicate); + } + } + }); + } } diff --git a/com.avaloq.tools.ddk.checkcfg.ui/src/com/avaloq/tools/ddk/checkcfg/ui/quickfix/Messages.java b/com.avaloq.tools.ddk.checkcfg.ui/src/com/avaloq/tools/ddk/checkcfg/ui/quickfix/Messages.java index 22dfc2036..3cb496a88 100644 --- a/com.avaloq.tools.ddk.checkcfg.ui/src/com/avaloq/tools/ddk/checkcfg/ui/quickfix/Messages.java +++ b/com.avaloq.tools.ddk.checkcfg.ui/src/com/avaloq/tools/ddk/checkcfg/ui/quickfix/Messages.java @@ -28,6 +28,8 @@ public class Messages extends NLS { public static String CheckCfgQuickfixProvider_REMOVE_DUPLICATE_CATALOG_LABEL; public static String CheckCfgQuickfixProvider_REMOVE_DUPLICATE_CHECK_DESCN; public static String CheckCfgQuickfixProvider_REMOVE_DUPLICATE_CHECK_LABEL; + public static String CheckCfgQuickfixProvider_REMOVE_DUPLICATE_LANG_DESCN; + public static String CheckCfgQuickfixProvider_REMOVE_DUPLICATE_LANG_LABEL; public static String CheckCfgQuickfixProvider_REMOVE_DUPLICATE_PARAM_DESCN; public static String CheckCfgQuickfixProvider_REMOVE_DUPLICATE_PARAM_LABEL; diff --git a/com.avaloq.tools.ddk.checkcfg.ui/src/com/avaloq/tools/ddk/checkcfg/ui/quickfix/messages.properties b/com.avaloq.tools.ddk.checkcfg.ui/src/com/avaloq/tools/ddk/checkcfg/ui/quickfix/messages.properties index 539316d64..33b296a44 100644 --- a/com.avaloq.tools.ddk.checkcfg.ui/src/com/avaloq/tools/ddk/checkcfg/ui/quickfix/messages.properties +++ b/com.avaloq.tools.ddk.checkcfg.ui/src/com/avaloq/tools/ddk/checkcfg/ui/quickfix/messages.properties @@ -8,5 +8,7 @@ CheckCfgQuickfixProvider_REMOVE_DUPLICATE_CATALOG_DESCN=Remove the duplicate cat CheckCfgQuickfixProvider_REMOVE_DUPLICATE_CATALOG_LABEL=Remove duplicate catalog CheckCfgQuickfixProvider_REMOVE_DUPLICATE_CHECK_DESCN=Remove the duplicate check configuration. CheckCfgQuickfixProvider_REMOVE_DUPLICATE_CHECK_LABEL=Remove duplicate check +CheckCfgQuickfixProvider_REMOVE_DUPLICATE_LANG_DESCN=Merge the duplicate language configurations into the first occurrence and remove the duplicates. +CheckCfgQuickfixProvider_REMOVE_DUPLICATE_LANG_LABEL=Merge duplicate language configurations CheckCfgQuickfixProvider_REMOVE_DUPLICATE_PARAM_DESCN=Remove the duplicate parameter configuration. CheckCfgQuickfixProvider_REMOVE_DUPLICATE_PARAM_LABEL=Remove duplicate parameter \ No newline at end of file