From 8b3138a881566fb498246e49376bc74ed3cba095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dinis=20Ferreira?= Date: Mon, 6 Jul 2026 12:36:20 +0100 Subject: [PATCH 1/3] refactor: migrate Xtend to Java - check.core (1/2: rename sources) Pure git mv of the 8 remaining .xtend sources to .java, content unchanged, so the rename edge has 100% similarity and git log --follow and git blame permanently traverse the migration boundary. This intermediate commit intentionally does not compile; the translation follows in the next commit. Co-Authored-By: Claude Opus 4.8 --- .../{CheckGeneratorConfig.xtend => CheckGeneratorConfig.java} | 0 .../formatting2/{CheckFormatter.xtend => CheckFormatter.java} | 0 .../check/generator/{CheckGenerator.xtend => CheckGenerator.java} | 0 ...eckGeneratorExtensions.xtend => CheckGeneratorExtensions.java} | 0 .../{CheckGeneratorNaming.xtend => CheckGeneratorNaming.java} | 0 .../{CheckJvmModelInferrer.xtend => CheckJvmModelInferrer.java} | 0 .../scoping/{CheckScopeProvider.xtend => CheckScopeProvider.java} | 0 .../typing/{CheckTypeComputer.xtend => CheckTypeComputer.java} | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/compiler/{CheckGeneratorConfig.xtend => CheckGeneratorConfig.java} (100%) rename com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/formatting2/{CheckFormatter.xtend => CheckFormatter.java} (100%) rename com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/{CheckGenerator.xtend => CheckGenerator.java} (100%) rename com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/{CheckGeneratorExtensions.xtend => CheckGeneratorExtensions.java} (100%) rename com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/{CheckGeneratorNaming.xtend => CheckGeneratorNaming.java} (100%) rename com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/jvmmodel/{CheckJvmModelInferrer.xtend => CheckJvmModelInferrer.java} (100%) rename com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/scoping/{CheckScopeProvider.xtend => CheckScopeProvider.java} (100%) rename com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/typing/{CheckTypeComputer.xtend => CheckTypeComputer.java} (100%) diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/compiler/CheckGeneratorConfig.xtend b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/compiler/CheckGeneratorConfig.java similarity index 100% rename from com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/compiler/CheckGeneratorConfig.xtend rename to com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/compiler/CheckGeneratorConfig.java diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/formatting2/CheckFormatter.xtend b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/formatting2/CheckFormatter.java similarity index 100% rename from com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/formatting2/CheckFormatter.xtend rename to com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/formatting2/CheckFormatter.java diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGenerator.xtend b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGenerator.java similarity index 100% rename from com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGenerator.xtend rename to com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGenerator.java diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorExtensions.xtend b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorExtensions.java similarity index 100% rename from com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorExtensions.xtend rename to com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorExtensions.java diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorNaming.xtend b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorNaming.java similarity index 100% rename from com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorNaming.xtend rename to com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorNaming.java diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/jvmmodel/CheckJvmModelInferrer.xtend b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/jvmmodel/CheckJvmModelInferrer.java similarity index 100% rename from com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/jvmmodel/CheckJvmModelInferrer.xtend rename to com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/jvmmodel/CheckJvmModelInferrer.java diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/scoping/CheckScopeProvider.xtend b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/scoping/CheckScopeProvider.java similarity index 100% rename from com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/scoping/CheckScopeProvider.xtend rename to com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/scoping/CheckScopeProvider.java diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/typing/CheckTypeComputer.xtend b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/typing/CheckTypeComputer.java similarity index 100% rename from com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/typing/CheckTypeComputer.xtend rename to com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/typing/CheckTypeComputer.java From db99559bb7cae8cce9a3b9032e9603d7588c3f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dinis=20Ferreira?= Date: Mon, 6 Jul 2026 12:36:48 +0100 Subject: [PATCH 2/3] refactor: migrate Xtend to Java - check.core (2/2: translate to Java 21) In-place translation of the 8 renamed sources, faithful to the Xtend compiler's own xtend-gen output (fresh ground-truth build). The three byte-critical emitters (CheckGeneratorExtensions, CheckGenerator, CheckJvmModelInferrer) keep the exact StringConcatenation call sequences incl. two-arg append(value, indent) and newLineIfNotEmpty. Notable per-file points: - CheckJvmModelInferrer: Xtend's JvmTypesBuilder '+=' silently skips null elements; the plain Iterables.addAll translation let null JvmMembers reach the type resolver (IllegalArgumentException: element: null, reproduced by the CheckValidationTest severity-range stubs). The two affected call sites are wrapped in IterableExtensions.filterNull like every sibling site. - CheckGeneratorExtensions.getContents: two deliberate output-neutral hardenings (explicit UTF-8; IllegalStateException wrapping of checked exceptions), documented in the PR. - splitCamelCase uses .formatted() per repo convention (behavior-identical to the String.format xtend-gen emitted). - CheckGeneratorNaming/CheckGenerator: Javadoc completed with @param/@return where originals carried plain block comments (checkstyle JavadocMethod gate). Co-Authored-By: Claude Opus 4.8 --- .../check/compiler/CheckGeneratorConfig.java | 23 +- .../ddk/check/formatting2/CheckFormatter.java | 614 +++++++--- .../ddk/check/generator/CheckGenerator.java | 591 ++++++--- .../generator/CheckGeneratorExtensions.java | 569 ++++++--- .../check/generator/CheckGeneratorNaming.java | 366 ++++-- .../check/jvmmodel/CheckJvmModelInferrer.java | 1091 ++++++++++------- .../ddk/check/scoping/CheckScopeProvider.java | 294 +++-- .../ddk/check/typing/CheckTypeComputer.java | 64 +- 8 files changed, 2408 insertions(+), 1204 deletions(-) diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/compiler/CheckGeneratorConfig.java b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/compiler/CheckGeneratorConfig.java index 7e7b9fc0f6..fd852a61d5 100644 --- a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/compiler/CheckGeneratorConfig.java +++ b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/compiler/CheckGeneratorConfig.java @@ -8,20 +8,27 @@ * Contributors: * Avaloq Group AG - initial API and implementation *******************************************************************************/ +package com.avaloq.tools.ddk.check.compiler; -package com.avaloq.tools.ddk.check.compiler +import org.eclipse.xtext.xbase.compiler.GeneratorConfig; -import org.eclipse.xtext.xbase.compiler.GeneratorConfig -import org.eclipse.xtend.lib.annotations.Accessors -class CheckGeneratorConfig extends GeneratorConfig { +@SuppressWarnings("nls") +public class CheckGeneratorConfig extends GeneratorConfig { - val String GENERATE_DOCUMENTATION_PROPERTY = "com.avaloq.tools.ddk.check.GenerateDocumentationForAllChecks" + private static final String GENERATE_DOCUMENTATION_PROPERTY = "com.avaloq.tools.ddk.check.GenerateDocumentationForAllChecks"; - @Accessors - boolean generateLanguageInternalChecks = false + private boolean generateLanguageInternalChecks; - def doGenerateDocumentationForAllChecks() { + public boolean doGenerateDocumentationForAllChecks() { return Boolean.parseBoolean(System.getProperty(GENERATE_DOCUMENTATION_PROPERTY)); } + + public boolean isGenerateLanguageInternalChecks() { + return generateLanguageInternalChecks; + } + + public void setGenerateLanguageInternalChecks(final boolean generateLanguageInternalChecks) { + this.generateLanguageInternalChecks = generateLanguageInternalChecks; + } } diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/formatting2/CheckFormatter.java b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/formatting2/CheckFormatter.java index 4a562733b0..c957748f1d 100644 --- a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/formatting2/CheckFormatter.java +++ b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/formatting2/CheckFormatter.java @@ -10,35 +10,74 @@ *******************************************************************************/ package com.avaloq.tools.ddk.check.formatting2; -import com.avaloq.tools.ddk.check.check.Category -import com.avaloq.tools.ddk.check.check.Check -import com.avaloq.tools.ddk.check.check.CheckCatalog -import com.avaloq.tools.ddk.check.check.Context -import com.avaloq.tools.ddk.check.check.ContextVariable -import com.avaloq.tools.ddk.check.check.FormalParameter -import com.avaloq.tools.ddk.check.check.Implementation -import com.avaloq.tools.ddk.check.check.Member -import com.avaloq.tools.ddk.check.check.SeverityRange -import com.avaloq.tools.ddk.check.check.XGuardExpression -import com.avaloq.tools.ddk.check.check.XIssueExpression -import com.avaloq.tools.ddk.check.services.CheckGrammarAccess -import com.google.inject.Inject -import org.eclipse.emf.ecore.EObject -import org.eclipse.xtext.formatting2.IFormattableDocument -import org.eclipse.xtext.formatting2.regionaccess.IEObjectRegion -import org.eclipse.xtext.xbase.XExpression -import org.eclipse.xtext.xbase.XIfExpression -import org.eclipse.xtext.xbase.XListLiteral -import org.eclipse.xtext.xbase.XMemberFeatureCall -import org.eclipse.xtext.xbase.XUnaryOperation -import org.eclipse.xtext.xbase.annotations.formatting2.XbaseWithAnnotationsFormatter -import org.eclipse.xtext.xbase.annotations.xAnnotations.XAnnotation -import org.eclipse.xtext.xtype.XImportDeclaration -import org.eclipse.xtext.xtype.XImportSection - -class CheckFormatter extends XbaseWithAnnotationsFormatter { - - @Inject extension CheckGrammarAccess +import java.util.Arrays; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.Keyword; +import org.eclipse.xtext.common.types.JvmFormalParameter; +import org.eclipse.xtext.common.types.JvmGenericArrayTypeReference; +import org.eclipse.xtext.common.types.JvmParameterizedTypeReference; +import org.eclipse.xtext.common.types.JvmTypeConstraint; +import org.eclipse.xtext.common.types.JvmTypeParameter; +import org.eclipse.xtext.common.types.JvmWildcardTypeReference; +import org.eclipse.xtext.formatting2.IFormattableDocument; +import org.eclipse.xtext.formatting2.IHiddenRegionFormatter; +import org.eclipse.xtext.formatting2.regionaccess.IEObjectRegion; +import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.xbase.XAssignment; +import org.eclipse.xtext.xbase.XBasicForLoopExpression; +import org.eclipse.xtext.xbase.XBinaryOperation; +import org.eclipse.xtext.xbase.XBlockExpression; +import org.eclipse.xtext.xbase.XCastedExpression; +import org.eclipse.xtext.xbase.XClosure; +import org.eclipse.xtext.xbase.XCollectionLiteral; +import org.eclipse.xtext.xbase.XConstructorCall; +import org.eclipse.xtext.xbase.XDoWhileExpression; +import org.eclipse.xtext.xbase.XExpression; +import org.eclipse.xtext.xbase.XFeatureCall; +import org.eclipse.xtext.xbase.XForLoopExpression; +import org.eclipse.xtext.xbase.XIfExpression; +import org.eclipse.xtext.xbase.XInstanceOfExpression; +import org.eclipse.xtext.xbase.XListLiteral; +import org.eclipse.xtext.xbase.XMemberFeatureCall; +import org.eclipse.xtext.xbase.XPostfixOperation; +import org.eclipse.xtext.xbase.XReturnExpression; +import org.eclipse.xtext.xbase.XSwitchExpression; +import org.eclipse.xtext.xbase.XSynchronizedExpression; +import org.eclipse.xtext.xbase.XThrowExpression; +import org.eclipse.xtext.xbase.XTryCatchFinallyExpression; +import org.eclipse.xtext.xbase.XTypeLiteral; +import org.eclipse.xtext.xbase.XUnaryOperation; +import org.eclipse.xtext.xbase.XVariableDeclaration; +import org.eclipse.xtext.xbase.XWhileExpression; +import org.eclipse.xtext.xbase.annotations.formatting2.XbaseWithAnnotationsFormatter; +import org.eclipse.xtext.xbase.annotations.xAnnotations.XAnnotation; +import org.eclipse.xtext.xbase.lib.XbaseGenerated; +import org.eclipse.xtext.xtype.XFunctionTypeRef; +import org.eclipse.xtext.xtype.XImportDeclaration; +import org.eclipse.xtext.xtype.XImportSection; + +import com.avaloq.tools.ddk.check.check.Category; +import com.avaloq.tools.ddk.check.check.Check; +import com.avaloq.tools.ddk.check.check.CheckCatalog; +import com.avaloq.tools.ddk.check.check.Context; +import com.avaloq.tools.ddk.check.check.ContextVariable; +import com.avaloq.tools.ddk.check.check.FormalParameter; +import com.avaloq.tools.ddk.check.check.Implementation; +import com.avaloq.tools.ddk.check.check.Member; +import com.avaloq.tools.ddk.check.check.SeverityRange; +import com.avaloq.tools.ddk.check.check.XGuardExpression; +import com.avaloq.tools.ddk.check.check.XIssueExpression; +import com.avaloq.tools.ddk.check.services.CheckGrammarAccess; +import com.google.inject.Inject; + + +@SuppressWarnings({"checkstyle:MethodName", "nls"}) +public class CheckFormatter extends XbaseWithAnnotationsFormatter { + + @Inject + private CheckGrammarAccess checkGrammarAccess; /** * Common formatting for curly brackets that are not handled by the parent formatter. @@ -48,254 +87,491 @@ class CheckFormatter extends XbaseWithAnnotationsFormatter { * @param document * the formattable document. */ - def private void formatCurlyBracket(EObject semanticElement, extension IFormattableDocument document) { + private void formatCurlyBracket(final EObject semanticElement, final IFormattableDocument document) { // low priority so that it can be overridden by other custom formatting rules. - val open = semanticElement.regionFor.keyword('{') - val close = semanticElement.regionFor.keyword('}') - interior(open, close)[lowPriority indent] - append(open)[lowPriority newLine] - prepend(close)[lowPriority newLine] + final ISemanticRegion open = regionFor(semanticElement).keyword("{"); + final ISemanticRegion close = regionFor(semanticElement).keyword("}"); + document.interior(open, close, (IHiddenRegionFormatter it) -> { + it.lowPriority(); + it.indent(); + }); + document.append(open, (IHiddenRegionFormatter it) -> { + it.lowPriority(); + it.newLine(); + }); + document.prepend(close, (IHiddenRegionFormatter it) -> { + it.lowPriority(); + it.newLine(); + }); } /** * Global formatting to be applied across the whole source. * - * @param checkcatalog + * @param requestRoot * the top level check catalog element. * @param document * the formattable document. */ - def private void globalFormatting(IEObjectRegion requestRoot, extension IFormattableDocument document) { + // CHECKSTYLE:CHECK-OFF MagicNumber + private void globalFormatting(final IEObjectRegion requestRoot, final IFormattableDocument document) { // autowrap everywhere. default to one-space between semantic regions. // low priority so that it can be overridden by other custom formatting rules. - var firstRegion = true - for(region : requestRoot.allSemanticRegions) { + boolean firstRegion = true; + for (ISemanticRegion region : requestRoot.getAllSemanticRegions()) { if (firstRegion) { - region.prepend[lowPriority autowrap(132)] - firstRegion = false + document.prepend(region, (IHiddenRegionFormatter it) -> { + it.lowPriority(); + it.autowrap(132); + }); + firstRegion = false; } else { - region.prepend[lowPriority oneSpace autowrap(132)] + document.prepend(region, (IHiddenRegionFormatter it) -> { + it.lowPriority(); + it.oneSpace(); + it.autowrap(132); + }); } } } - - def dispatch void format(CheckCatalog checkcatalog, extension IFormattableDocument document) { - prepend(checkcatalog)[noSpace newLines=0] - append(checkcatalog)[noSpace setNewLines(0, 0, 1)] - val finalKw = checkcatalog.regionFor.keyword('final') - val catalog = checkcatalog.regionFor.keyword('catalog') - if (finalKw !== null) { - prepend(finalKw)[setNewLines(1, 2, 2)] + // CHECKSTYLE:CHECK-ON MagicNumber + + protected void _format(final CheckCatalog checkcatalog, final IFormattableDocument document) { + document.prepend(checkcatalog, (IHiddenRegionFormatter it) -> { + it.noSpace(); + it.setNewLines(0); + }); + document.append(checkcatalog, (IHiddenRegionFormatter it) -> { + it.noSpace(); + it.setNewLines(0, 0, 1); + }); + final ISemanticRegion finalKw = regionFor(checkcatalog).keyword("final"); + final ISemanticRegion catalog = regionFor(checkcatalog).keyword("catalog"); + if (finalKw != null) { + document.prepend(finalKw, (IHiddenRegionFormatter it) -> { + it.setNewLines(1, 2, 2); + }); } else { - prepend(catalog)[setNewLines(1, 1, 2)] + document.prepend(catalog, (IHiddenRegionFormatter it) -> { + it.setNewLines(1, 1, 2); + }); } - val forKw = checkcatalog.regionFor.keyword('for') - prepend(forKw)[setNewLines(1, 1, 2)] - formatCurlyBracket(checkcatalog, document) + final ISemanticRegion forKw = regionFor(checkcatalog).keyword("for"); + document.prepend(forKw, (IHiddenRegionFormatter it) -> { + it.setNewLines(1, 1, 2); + }); + formatCurlyBracket(checkcatalog, document); // Generated model traversal - format(checkcatalog.getImports(), document); + this.format(checkcatalog.getImports(), document); for (Category categories : checkcatalog.getCategories()) { - format(categories, document); + this.format(categories, document); } for (Implementation implementations : checkcatalog.getImplementations()) { - format(implementations, document); + this.format(implementations, document); } for (Check checks : checkcatalog.getChecks()) { - format(checks, document); + this.format(checks, document); } for (Member members : checkcatalog.getMembers()) { - format(members, document); + this.format(members, document); } // ADDED: only fill in the gaps after any high priority formatting has been applied. - textRegionAccess.regionForRootEObject?.globalFormatting(document) + IEObjectRegion rootRegion = getTextRegionAccess().regionForRootEObject(); + if (rootRegion != null) { + globalFormatting(rootRegion, document); + } } - override dispatch void format(XImportSection ximportsection, extension IFormattableDocument document) { + @Override + protected void _format(final XImportSection ximportsection, final IFormattableDocument document) { // Generated model traversal for (XImportDeclaration importDeclarations : ximportsection.getImportDeclarations()) { // ADDED: formatting added before each import - prepend(importDeclarations)[setNewLines(1, 1, 2)] + document.prepend(importDeclarations, (IHiddenRegionFormatter it) -> { + it.setNewLines(1, 1, 2); + }); - format(importDeclarations, document); + this.format(importDeclarations, document); } } - def dispatch void format(Category category, extension IFormattableDocument document) { - prepend(category)[setNewLines(1, 2, 2)] - formatCurlyBracket(category, document) + protected void _format(final Category category, final IFormattableDocument document) { + document.prepend(category, (IHiddenRegionFormatter it) -> { + it.setNewLines(1, 2, 2); + }); + formatCurlyBracket(category, document); // Generated model traversal for (Check checks : category.getChecks()) { - format(checks, document); + this.format(checks, document); } } - def dispatch void format(Check check, extension IFormattableDocument document) { - prepend(check)[setNewLines(1, 2, 2)] - val open = check.regionFor.keyword('(') - val close = check.regionFor.keyword(')') - interior(open, close)[highPriority noSpace] // High priority to override formatting from adjacent regions and parent formatter. - val message = check.regionFor.keyword('message') - prepend(message)[setNewLines(1, 1, 2)] - formatCurlyBracket(check, document) + protected void _format(final Check check, final IFormattableDocument document) { + document.prepend(check, (IHiddenRegionFormatter it) -> { + it.setNewLines(1, 2, 2); + }); + final ISemanticRegion open = regionFor(check).keyword("("); + final ISemanticRegion close = regionFor(check).keyword(")"); + document.interior(open, close, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.noSpace(); + }); // High priority to override formatting from adjacent regions and parent formatter. + final ISemanticRegion message = regionFor(check).keyword("message"); + document.prepend(message, (IHiddenRegionFormatter it) -> { + it.setNewLines(1, 1, 2); + }); + formatCurlyBracket(check, document); // Generated model traversal - format(check.getSeverityRange(), document); + this.format(check.getSeverityRange(), document); for (FormalParameter formalParameters : check.getFormalParameters()) { // ADDED: formatting added around comma. // High priority to override formatting from adjacent regions and parent formatter. - val comma = immediatelyFollowing(formalParameters).keyword(',') - prepend(comma)[highPriority noSpace] - append(comma)[highPriority setNewLines(0, 0, 1)] - - format(formalParameters, document); + final ISemanticRegion comma = immediatelyFollowing(formalParameters).keyword(","); + document.prepend(comma, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.noSpace(); + }); + document.append(comma, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.setNewLines(0, 0, 1); + }); + + this.format(formalParameters, document); } for (Context contexts : check.getContexts()) { - format(contexts, document); + this.format(contexts, document); } } - def dispatch void format(SeverityRange severityrange, extension IFormattableDocument document) { - val range = severityrange.regionFor.keyword('SeverityRange') - surround(range)[noSpace] - val open = severityrange.regionFor.keyword('(') - append(open)[noSpace] - val close = severityrange.regionFor.keyword(')') - prepend(close)[noSpace] - append(close)[newLine] + protected void _format(final SeverityRange severityrange, final IFormattableDocument document) { + final ISemanticRegion range = regionFor(severityrange).keyword("SeverityRange"); + document.surround(range, (IHiddenRegionFormatter it) -> { + it.noSpace(); + }); + final ISemanticRegion open = regionFor(severityrange).keyword("("); + document.append(open, (IHiddenRegionFormatter it) -> { + it.noSpace(); + }); + final ISemanticRegion close = regionFor(severityrange).keyword(")"); + document.prepend(close, (IHiddenRegionFormatter it) -> { + it.noSpace(); + }); + document.append(close, (IHiddenRegionFormatter it) -> { + it.newLine(); + }); } - def dispatch void format(Member member, extension IFormattableDocument document) { + protected void _format(final Member member, final IFormattableDocument document) { // Generated model traversal for (XAnnotation annotations : member.getAnnotations()) { - format(annotations, document); + this.format(annotations, document); } - format(member.getType(), document); - format(member.getValue(), document); + this.format(member.getType(), document); + this.format(member.getValue(), document); } - def dispatch void format(Implementation implementation, extension IFormattableDocument document) { - prepend(implementation)[setNewLines(1, 2, 2)] + protected void _format(final Implementation implementation, final IFormattableDocument document) { + document.prepend(implementation, (IHiddenRegionFormatter it) -> { + it.setNewLines(1, 2, 2); + }); // Generated model traversal - format(implementation.getContext(), document); + this.format(implementation.getContext(), document); } - def dispatch void format(FormalParameter formalparameter, extension IFormattableDocument document) { + protected void _format(final FormalParameter formalparameter, final IFormattableDocument document) { // Generated model traversal - format(formalparameter.getType(), document); - format(formalparameter.getRight(), document); + this.format(formalparameter.getType(), document); + this.format(formalparameter.getRight(), document); } - def dispatch void format(XUnaryOperation xunaryoperation, extension IFormattableDocument document) { + protected void _format(final XUnaryOperation xunaryoperation, final IFormattableDocument document) { // Generated model traversal - format(xunaryoperation.getOperand(), document); + this.format(xunaryoperation.getOperand(), document); } - def dispatch void format(XListLiteral xlistliteral, extension IFormattableDocument document) { + protected void _format(final XListLiteral xlistliteral, final IFormattableDocument document) { // Generated model traversal for (XExpression elements : xlistliteral.getElements()) { - format(elements, document); + this.format(elements, document); } } - def dispatch void format(Context context, extension IFormattableDocument document) { - surround(context)[setNewLines(1, 2, 2)] + protected void _format(final Context context, final IFormattableDocument document) { + document.surround(context, (IHiddenRegionFormatter it) -> { + it.setNewLines(1, 2, 2); + }); // Generated model traversal - format(context.getContextVariable(), document); - format(context.getConstraint(), document); + this.format(context.getContextVariable(), document); + this.format(context.getConstraint(), document); } - def dispatch void format(ContextVariable contextvariable, extension IFormattableDocument document) { + protected void _format(final ContextVariable contextvariable, final IFormattableDocument document) { // Generated model traversal - format(contextvariable.getType(), document); + this.format(contextvariable.getType(), document); } - def dispatch void format(XGuardExpression xguardexpression, extension IFormattableDocument document) { - prepend(xguardexpression)[setNewLines(1, 2, 2)] + protected void _format(final XGuardExpression xguardexpression, final IFormattableDocument document) { + document.prepend(xguardexpression, (IHiddenRegionFormatter it) -> { + it.setNewLines(1, 2, 2); + }); // Generated model traversal - format(xguardexpression.getGuard(), document); + this.format(xguardexpression.getGuard(), document); } - def dispatch void format(XIssueExpression xissueexpression, extension IFormattableDocument document) { + protected void _format(final XIssueExpression xissueexpression, final IFormattableDocument document) { // High priority to override formatting from adjacent regions and parent formatter. - prepend(xissueexpression)[highPriority setNewLines(1, 2, 2)] - XIssueExpressionAccess.findKeywords('#').forEach[ - val hash = xissueexpression.regionFor.keyword(it) - surround(hash)[highPriority noSpace] - ] - val openSquare = xissueexpression.regionFor.keyword('[') - surround(openSquare)[highPriority noSpace] - val closeSquare = xissueexpression.regionFor.keyword(']') - prepend(closeSquare)[highPriority noSpace] - XIssueExpressionAccess.findKeywords('(').forEach[ - val open = xissueexpression.regionFor.keyword(it) - append(open)[highPriority noSpace] - ] - XIssueExpressionAccess.findKeywords(')').forEach[ - val close = xissueexpression.regionFor.keyword(it) - prepend(close)[highPriority noSpace] - ] + document.prepend(xissueexpression, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.setNewLines(1, 2, 2); + }); + checkGrammarAccess.getXIssueExpressionAccess().findKeywords("#").forEach((Keyword kw) -> { + final ISemanticRegion hash = regionFor(xissueexpression).keyword(kw); + document.surround(hash, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.noSpace(); + }); + }); + final ISemanticRegion openSquare = regionFor(xissueexpression).keyword("["); + document.surround(openSquare, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.noSpace(); + }); + final ISemanticRegion closeSquare = regionFor(xissueexpression).keyword("]"); + document.prepend(closeSquare, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.noSpace(); + }); + checkGrammarAccess.getXIssueExpressionAccess().findKeywords("(").forEach((Keyword kw) -> { + final ISemanticRegion open = regionFor(xissueexpression).keyword(kw); + document.append(open, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.noSpace(); + }); + }); + checkGrammarAccess.getXIssueExpressionAccess().findKeywords(")").forEach((Keyword kw) -> { + final ISemanticRegion close = regionFor(xissueexpression).keyword(kw); + document.prepend(close, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.noSpace(); + }); + }); // Generated model traversal - format(xissueexpression.getMarkerObject(), document); - format(xissueexpression.getMarkerIndex(), document); - format(xissueexpression.getMessage(), document); + this.format(xissueexpression.getMarkerObject(), document); + this.format(xissueexpression.getMarkerIndex(), document); + this.format(xissueexpression.getMessage(), document); for (XExpression messageParameters : xissueexpression.getMessageParameters()) { // ADDED: formatting added around comma - val comma = immediatelyFollowing(messageParameters).keyword(',') - prepend(comma)[highPriority noSpace] - append(comma)[highPriority oneSpace] - - format(messageParameters, document); + final ISemanticRegion comma = immediatelyFollowing(messageParameters).keyword(","); + document.prepend(comma, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.noSpace(); + }); + document.append(comma, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.oneSpace(); + }); + + this.format(messageParameters, document); } for (XExpression issueData : xissueexpression.getIssueData()) { // ADDED: formatting added around comma - val comma = immediatelyFollowing(issueData).keyword(',') - prepend(comma)[highPriority noSpace] - append(comma)[highPriority oneSpace] - - format(issueData, document); + final ISemanticRegion comma = immediatelyFollowing(issueData).keyword(","); + document.prepend(comma, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.noSpace(); + }); + document.append(comma, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.oneSpace(); + }); + + this.format(issueData, document); } } - override dispatch void format(XIfExpression xifexpression, extension IFormattableDocument document) { + @Override + protected void _format(final XIfExpression xifexpression, final IFormattableDocument document) { // High priority to override formatting from adjacent regions and parent formatter. - prepend(xifexpression)[highPriority setNewLines(1, 1, 2)] - val open = xifexpression.regionFor.keyword('(') - val close = xifexpression.regionFor.keyword(')') - prepend(open)[highPriority oneSpace] - append(open)[highPriority noSpace] - prepend(close)[highPriority noSpace] - append(close)[highPriority newLines=0 oneSpace] - val elseKw = xifexpression.regionFor.keyword('else') - surround(elseKw)[highPriority newLines=0 oneSpace] + document.prepend(xifexpression, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.setNewLines(1, 1, 2); + }); + final ISemanticRegion open = regionFor(xifexpression).keyword("("); + final ISemanticRegion close = regionFor(xifexpression).keyword(")"); + document.prepend(open, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.oneSpace(); + }); + document.append(open, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.noSpace(); + }); + document.prepend(close, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.noSpace(); + }); + document.append(close, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.setNewLines(0); + it.oneSpace(); + }); + final ISemanticRegion elseKw = regionFor(xifexpression).keyword("else"); + document.surround(elseKw, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.setNewLines(0); + it.oneSpace(); + }); // defer to super class for model traversal - super._format(xifexpression, document) + super._format(xifexpression, document); } - override dispatch void format(XMemberFeatureCall xfeaturecall, extension IFormattableDocument document) { + @Override + protected void _format(final XMemberFeatureCall xfeaturecall, final IFormattableDocument document) { // set no space after '::' in CheckUtil::hasQualifiedName(..., and also not after plain "." or "?." // High priority to override formatting from adjacent regions and parent formatter. - XMemberFeatureCallAccess.findKeywords('.').forEach[ - val dot = xfeaturecall.regionFor.keyword(it) - append(dot)[highPriority noSpace] - ] - XMemberFeatureCallAccess.findKeywords('?.').forEach[ - val queryDot = xfeaturecall.regionFor.keyword(it) - append(queryDot)[highPriority noSpace] - ] - XMemberFeatureCallAccess.findKeywords('::').forEach[ - val colonColon = xfeaturecall.regionFor.keyword(it) - append(colonColon)[highPriority noSpace] - ] + checkGrammarAccess.getXMemberFeatureCallAccess().findKeywords(".").forEach((Keyword kw) -> { + final ISemanticRegion dot = regionFor(xfeaturecall).keyword(kw); + document.append(dot, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.noSpace(); + }); + }); + checkGrammarAccess.getXMemberFeatureCallAccess().findKeywords("?.").forEach((Keyword kw) -> { + final ISemanticRegion queryDot = regionFor(xfeaturecall).keyword(kw); + document.append(queryDot, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.noSpace(); + }); + }); + checkGrammarAccess.getXMemberFeatureCallAccess().findKeywords("::").forEach((Keyword kw) -> { + final ISemanticRegion colonColon = regionFor(xfeaturecall).keyword(kw); + document.append(colonColon, (IHiddenRegionFormatter it) -> { + it.highPriority(); + it.noSpace(); + }); + }); // defer to super class for model traversal - super._format(xfeaturecall, document) + super._format(xfeaturecall, document); + } + + @Override + @XbaseGenerated + public void format(final Object xlistliteral, final IFormattableDocument document) { + if (xlistliteral instanceof JvmTypeParameter) { + _format((JvmTypeParameter) xlistliteral, document); + } else if (xlistliteral instanceof JvmFormalParameter) { + _format((JvmFormalParameter) xlistliteral, document); + } else if (xlistliteral instanceof XtextResource) { + _format((XtextResource) xlistliteral, document); + } else if (xlistliteral instanceof XAssignment) { + _format((XAssignment) xlistliteral, document); + } else if (xlistliteral instanceof XBinaryOperation) { + _format((XBinaryOperation) xlistliteral, document); + } else if (xlistliteral instanceof XDoWhileExpression) { + _format((XDoWhileExpression) xlistliteral, document); + } else if (xlistliteral instanceof XFeatureCall) { + _format((XFeatureCall) xlistliteral, document); + } else if (xlistliteral instanceof XListLiteral) { + _format((XListLiteral) xlistliteral, document); + } else if (xlistliteral instanceof XMemberFeatureCall) { + _format((XMemberFeatureCall) xlistliteral, document); + } else if (xlistliteral instanceof XPostfixOperation) { + _format((XPostfixOperation) xlistliteral, document); + } else if (xlistliteral instanceof XUnaryOperation) { + _format((XUnaryOperation) xlistliteral, document); + } else if (xlistliteral instanceof XWhileExpression) { + _format((XWhileExpression) xlistliteral, document); + } else if (xlistliteral instanceof XFunctionTypeRef) { + _format((XFunctionTypeRef) xlistliteral, document); + } else if (xlistliteral instanceof Category) { + _format((Category) xlistliteral, document); + } else if (xlistliteral instanceof Check) { + _format((Check) xlistliteral, document); + } else if (xlistliteral instanceof CheckCatalog) { + _format((CheckCatalog) xlistliteral, document); + } else if (xlistliteral instanceof Context) { + _format((Context) xlistliteral, document); + } else if (xlistliteral instanceof Implementation) { + _format((Implementation) xlistliteral, document); + } else if (xlistliteral instanceof Member) { + _format((Member) xlistliteral, document); + } else if (xlistliteral instanceof XGuardExpression) { + _format((XGuardExpression) xlistliteral, document); + } else if (xlistliteral instanceof XIssueExpression) { + _format((XIssueExpression) xlistliteral, document); + } else if (xlistliteral instanceof JvmGenericArrayTypeReference) { + _format((JvmGenericArrayTypeReference) xlistliteral, document); + } else if (xlistliteral instanceof JvmParameterizedTypeReference) { + _format((JvmParameterizedTypeReference) xlistliteral, document); + } else if (xlistliteral instanceof JvmWildcardTypeReference) { + _format((JvmWildcardTypeReference) xlistliteral, document); + } else if (xlistliteral instanceof XBasicForLoopExpression) { + _format((XBasicForLoopExpression) xlistliteral, document); + } else if (xlistliteral instanceof XBlockExpression) { + _format((XBlockExpression) xlistliteral, document); + } else if (xlistliteral instanceof XCastedExpression) { + _format((XCastedExpression) xlistliteral, document); + } else if (xlistliteral instanceof XClosure) { + _format((XClosure) xlistliteral, document); + } else if (xlistliteral instanceof XCollectionLiteral) { + _format((XCollectionLiteral) xlistliteral, document); + } else if (xlistliteral instanceof XConstructorCall) { + _format((XConstructorCall) xlistliteral, document); + } else if (xlistliteral instanceof XForLoopExpression) { + _format((XForLoopExpression) xlistliteral, document); + } else if (xlistliteral instanceof XIfExpression) { + _format((XIfExpression) xlistliteral, document); + } else if (xlistliteral instanceof XInstanceOfExpression) { + _format((XInstanceOfExpression) xlistliteral, document); + } else if (xlistliteral instanceof XReturnExpression) { + _format((XReturnExpression) xlistliteral, document); + } else if (xlistliteral instanceof XSwitchExpression) { + _format((XSwitchExpression) xlistliteral, document); + } else if (xlistliteral instanceof XSynchronizedExpression) { + _format((XSynchronizedExpression) xlistliteral, document); + } else if (xlistliteral instanceof XThrowExpression) { + _format((XThrowExpression) xlistliteral, document); + } else if (xlistliteral instanceof XTryCatchFinallyExpression) { + _format((XTryCatchFinallyExpression) xlistliteral, document); + } else if (xlistliteral instanceof XTypeLiteral) { + _format((XTypeLiteral) xlistliteral, document); + } else if (xlistliteral instanceof XVariableDeclaration) { + _format((XVariableDeclaration) xlistliteral, document); + } else if (xlistliteral instanceof XAnnotation) { + _format((XAnnotation) xlistliteral, document); + } else if (xlistliteral instanceof ContextVariable) { + _format((ContextVariable) xlistliteral, document); + } else if (xlistliteral instanceof FormalParameter) { + _format((FormalParameter) xlistliteral, document); + } else if (xlistliteral instanceof SeverityRange) { + _format((SeverityRange) xlistliteral, document); + } else if (xlistliteral instanceof JvmTypeConstraint) { + _format((JvmTypeConstraint) xlistliteral, document); + } else if (xlistliteral instanceof XExpression) { + _format((XExpression) xlistliteral, document); + } else if (xlistliteral instanceof XImportDeclaration) { + _format((XImportDeclaration) xlistliteral, document); + } else if (xlistliteral instanceof XImportSection) { + _format((XImportSection) xlistliteral, document); + } else if (xlistliteral instanceof EObject) { + _format((EObject) xlistliteral, document); + } else if (xlistliteral == null) { + _format((Void) null, document); + } else if (xlistliteral != null) { + _format(xlistliteral, document); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + + Arrays.asList(xlistliteral, document).toString()); + } } } diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGenerator.java b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGenerator.java index 083ddc9b25..57bc818839 100644 --- a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGenerator.java +++ b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGenerator.java @@ -8,210 +8,449 @@ * Contributors: * Avaloq Group AG - initial API and implementation *******************************************************************************/ -package com.avaloq.tools.ddk.check.generator - -import com.avaloq.tools.ddk.check.check.CheckCatalog -import com.avaloq.tools.ddk.check.util.CheckUtil -import com.google.inject.Inject -import org.eclipse.emf.ecore.resource.Resource -import org.eclipse.xtext.generator.AbstractFileSystemAccess -import org.eclipse.xtext.generator.IFileSystemAccess -import org.eclipse.xtext.generator.IFileSystemAccess2 -import org.eclipse.xtext.xbase.compiler.JvmModelGenerator - -import static org.eclipse.xtext.xbase.lib.IteratorExtensions.* -import com.avaloq.tools.ddk.check.check.FormalParameter -import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable -import org.eclipse.xtext.common.types.JvmField -import org.eclipse.xtext.xbase.compiler.GeneratorConfig - -import static extension com.avaloq.tools.ddk.check.generator.CheckGeneratorExtensions.* -import static extension com.avaloq.tools.ddk.check.generator.CheckGeneratorNaming.* -import com.avaloq.tools.ddk.check.compiler.ICheckGeneratorConfigProvider - -class CheckGenerator extends JvmModelGenerator { - - @Inject extension CheckGeneratorExtensions generatorExtensions - @Inject extension CheckGeneratorNaming - @Inject CheckCompiler compiler - @Inject ICheckGeneratorConfigProvider generatorConfigProvider; - - override void doGenerate(Resource resource, IFileSystemAccess fsa) { - val lfFsa = new LfNormalizingFileSystemAccess(fsa as IFileSystemAccess2) - super.doGenerate(resource, lfFsa); // Generate validator, catalog, and preference initializer from inferred Jvm models. - val config = generatorConfigProvider.get(resource?.URI); - for (catalog : toIterable(resource.allContents).filter(typeof(CheckCatalog))) { +package com.avaloq.tools.ddk.check.generator; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.xtend2.lib.StringConcatenation; +import org.eclipse.xtext.common.types.JvmField; +import org.eclipse.xtext.generator.AbstractFileSystemAccess; +import org.eclipse.xtext.generator.IFileSystemAccess; +import org.eclipse.xtext.generator.IFileSystemAccess2; +import org.eclipse.xtext.generator.OutputConfiguration; +import org.eclipse.xtext.xbase.compiler.GeneratorConfig; +import org.eclipse.xtext.xbase.compiler.JvmModelGenerator; +import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable; +import org.eclipse.xtext.xbase.lib.Functions.Function1; +import org.eclipse.xtext.xbase.lib.IterableExtensions; +import org.eclipse.xtext.xbase.lib.IteratorExtensions; +import org.eclipse.xtext.xbase.lib.StringExtensions; + +import com.avaloq.tools.ddk.check.check.Category; +import com.avaloq.tools.ddk.check.check.Check; +import com.avaloq.tools.ddk.check.check.CheckCatalog; +import com.avaloq.tools.ddk.check.check.FormalParameter; +import com.avaloq.tools.ddk.check.check.XIssueExpression; +import com.avaloq.tools.ddk.check.compiler.CheckGeneratorConfig; +import com.avaloq.tools.ddk.check.compiler.ICheckGeneratorConfigProvider; +import com.avaloq.tools.ddk.check.util.CheckUtil; +import com.google.common.collect.Iterables; +import com.google.inject.Inject; + + +/** + * Generates the artifacts (validator, catalog, issue codes, standalone setup, service registry and documentation) + * for a Check Catalog. + */ +@SuppressWarnings({"checkstyle:MethodName", "nls"}) +public class CheckGenerator extends JvmModelGenerator { + + @Inject + private CheckGeneratorExtensions generatorExtensions; + + @Inject + private CheckGeneratorNaming checkGeneratorNaming; + + @Inject + private CheckCompiler compiler; - lfFsa.generateFile(catalog.issueCodesFilePath, catalog.compileIssueCodes) - lfFsa.generateFile(catalog.standaloneSetupPath, catalog.compileStandaloneSetup) + @Inject + private ICheckGeneratorConfigProvider generatorConfigProvider; + + @Override + public void doGenerate(final Resource resource, final IFileSystemAccess fsa) { + final LfNormalizingFileSystemAccess lfFsa = new LfNormalizingFileSystemAccess((IFileSystemAccess2) fsa); + super.doGenerate(resource, lfFsa); // Generate validator, catalog, and preference initializer from inferred Jvm models. + URI uri = null; + if (resource != null) { + uri = resource.getURI(); + } + final CheckGeneratorConfig config = generatorConfigProvider.get(uri); + final Iterable catalogs = Iterables.filter(IteratorExtensions.toIterable(resource.getAllContents()), CheckCatalog.class); + for (final CheckCatalog catalog : catalogs) { + lfFsa.generateFile(checkGeneratorNaming.issueCodesFilePath(catalog), compileIssueCodes(catalog)); + lfFsa.generateFile(checkGeneratorNaming.standaloneSetupPath(catalog), compileStandaloneSetup(catalog)); // change output path for service registry lfFsa.generateFile( - CheckUtil::serviceRegistryClassName, - CheckGeneratorConstants::CHECK_REGISTRY_OUTPUT, - catalog.generateServiceRegistry(CheckUtil::serviceRegistryClassName, fsa) - ) + CheckUtil.serviceRegistryClassName(), + CheckGeneratorConstants.CHECK_REGISTRY_OUTPUT, + generateServiceRegistry(catalog, CheckUtil.serviceRegistryClassName(), fsa)); // generate documentation for SCA-checks only - if(config !== null && (config.doGenerateDocumentationForAllChecks || !config.generateLanguageInternalChecks)){ + if (config != null && (config.doGenerateDocumentationForAllChecks() || !config.isGenerateLanguageInternalChecks())) { // change output path for html files to docs/ - lfFsa.generateFile(catalog.docFileName, CheckGeneratorConstants::CHECK_DOC_OUTPUT, catalog.compileDoc) + lfFsa.generateFile(checkGeneratorNaming.docFileName(catalog), CheckGeneratorConstants.CHECK_DOC_OUTPUT, compileDoc(catalog)); } } } - /* Documentation compiler, generates HTML output. */ - def compileDoc (CheckCatalog catalog)''' - «val body = bodyDoc(catalog)» - - - - - - «catalog.name» - - - -

Check Catalog «catalog.name»

- «val formattedDescription = catalog.description.formatDescription» - «IF formattedDescription !== null» -

«formattedDescription»

- «ENDIF» - «body» - - - - ''' - - def bodyDoc(CheckCatalog catalog)''' - «FOR check:catalog.checks» -

«check.label» («check.defaultSeverity.name().toLowerCase»)

- «val formattedCheckDescription = check.description.formatDescription» - «IF formattedCheckDescription !== null» - «formattedCheckDescription» - «ENDIF» -

Message: «check.message.replacePlaceholder»


- «ENDFOR» - «FOR category:catalog.categories» -
-

«category.label»

- «val formattedCateogryDescription = category.description.formatDescription» - «IF formattedCateogryDescription !== null» - «formattedCateogryDescription» - «ENDIF» - «FOR check:category.checks» -
-

«check.label» («check.defaultSeverity.name().toLowerCase»)

- «val formattedCheckDescription = check.description.formatDescription» - «IF formattedCheckDescription !== null» - «formattedCheckDescription» - «ENDIF» -

Message: «check.message.replacePlaceholder»

-
- «ENDFOR» -
- «ENDFOR» - ''' - - /* - * Creates an IssueCodes file for a Check Catalog. Every Check Catalog will have its own file - * of issue codes. - */ - def compileIssueCodes(CheckCatalog catalog) { - val allIssues = catalog.checkAndImplementationIssues // all Issue instances - val allIssueNames = allIssues.toMap([issue|issue.issueCode()], [issue|issue.issueName()]) // *all* issue names, unordered - - ''' - «IF !(catalog.packageName.isNullOrEmpty)» - package «catalog.packageName»; - «ENDIF» - - /** - * Issue codes which may be used to address validation issues (for instance in quickfixes). - */ - @SuppressWarnings("all") - public final class «catalog.issueCodesClassName» { - - «FOR issueCode:allIssueNames.keySet.sort» - public static final String «issueCode» = "«issueCodeValue(catalog, allIssueNames.get(issueCode))»"; - «ENDFOR» - - private «catalog.issueCodesClassName»() { - // Prevent instantiation. + // CHECKSTYLE:CONSTANTS-OFF the repeated literals are fragments of the emitted HTML/Java source, not nameable constants + /** + * Documentation compiler, generates HTML output. + * + * @param catalog + * the check catalog + * @return the HTML documentation + */ + public CharSequence compileDoc(final CheckCatalog catalog) { + StringConcatenation builder = new StringConcatenation(); + final CharSequence body = bodyDoc(catalog); + builder.newLineIfNotEmpty(); + builder.append(""); + builder.newLine(); + builder.append(""); + builder.newLine(); + builder.append(""); + builder.newLine(); + builder.append(" "); + builder.append(""); + builder.newLine(); + builder.append(" "); + builder.append(""); + builder.newLine(); + builder.append(" "); + builder.append(""); + builder.append(catalog.getName(), " "); + builder.append(""); + builder.newLineIfNotEmpty(); + builder.append(""); + builder.newLine(); + builder.newLine(); + builder.append(""); + builder.newLine(); + builder.append(" "); + builder.append("

Check Catalog "); + builder.append(catalog.getName(), " "); + builder.append("

"); + builder.newLineIfNotEmpty(); + builder.append(" "); + final String formattedDescription = generatorExtensions.formatDescription(catalog.getDescription()); + builder.newLineIfNotEmpty(); + if (formattedDescription != null) { + builder.append(" "); + builder.append("

"); + builder.append(formattedDescription, " "); + builder.append("

"); + builder.newLineIfNotEmpty(); + } + builder.append(" "); + builder.append(body, " "); + builder.newLineIfNotEmpty(); + builder.append(""); + builder.newLine(); + builder.newLine(); + builder.append(""); + builder.newLine(); + return builder; + } + + public CharSequence bodyDoc(final CheckCatalog catalog) { + StringConcatenation builder = new StringConcatenation(); + for (final Check check : catalog.getChecks()) { + builder.append("

"); + builder.append(check.getLabel()); + builder.append(" ("); + builder.append(check.getDefaultSeverity().name().toLowerCase()); + builder.append(")

"); + builder.newLineIfNotEmpty(); + final String formattedCheckDescription = generatorExtensions.formatDescription(check.getDescription()); + builder.newLineIfNotEmpty(); + if (formattedCheckDescription != null) { + builder.append(formattedCheckDescription); + builder.newLineIfNotEmpty(); } + builder.append("

Message: "); + builder.append(generatorExtensions.replacePlaceholder(check.getMessage())); + builder.append("


"); + builder.newLineIfNotEmpty(); } - ''' + for (final Category category : catalog.getCategories()) { + builder.append("
"); + builder.newLine(); + builder.append(" "); + builder.append("

"); + builder.append(category.getLabel(), " "); + builder.append("

"); + builder.newLineIfNotEmpty(); + builder.append(" "); + final String formattedCateogryDescription = generatorExtensions.formatDescription(category.getDescription()); + builder.newLineIfNotEmpty(); + if (formattedCateogryDescription != null) { + builder.append(" "); + builder.append(formattedCateogryDescription, " "); + builder.newLineIfNotEmpty(); + } + for (final Check check : category.getChecks()) { + builder.append(" "); + builder.append("
"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append("

"); + builder.append(check.getLabel(), " "); + builder.append(" ("); + builder.append(check.getDefaultSeverity().name().toLowerCase(), " "); + builder.append(")

"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + final String formattedCheckDescription = generatorExtensions.formatDescription(check.getDescription()); + builder.newLineIfNotEmpty(); + if (formattedCheckDescription != null) { + builder.append(" "); + builder.append(" "); + builder.append(formattedCheckDescription, " "); + builder.newLineIfNotEmpty(); + } + builder.append(" "); + builder.append(" "); + builder.append("

Message: "); + builder.append(generatorExtensions.replacePlaceholder(check.getMessage()), " "); + builder.append("

"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("
"); + builder.newLine(); + } + builder.append("
"); + builder.newLine(); + } + return builder; } - /* - * Generates the Java standalone setup class which will be called by the ServiceRegistry. + /** + * Creates an IssueCodes file for a Check Catalog. Every Check Catalog will have its own file + * of issue codes. + * + * @param catalog + * the check catalog + * @return the issue codes class source */ - def compileStandaloneSetup(CheckCatalog catalog) { - ''' - «IF !(catalog.packageName.isNullOrEmpty)» - package «catalog.packageName»; - «ENDIF» - - import org.apache.logging.log4j.Logger; - import org.apache.logging.log4j.LogManager; - - import com.avaloq.tools.ddk.check.runtime.configuration.ModelLocation; - import com.avaloq.tools.ddk.check.runtime.registry.ICheckCatalogRegistry; - import com.avaloq.tools.ddk.check.runtime.registry.ICheckValidatorRegistry; - import com.avaloq.tools.ddk.check.runtime.registry.ICheckValidatorStandaloneSetup; - - /** - * Standalone setup for «catalog.name» as required by the standalone builder. - */ - @SuppressWarnings("nls") - public class «catalog.standaloneSetupClassName» implements ICheckValidatorStandaloneSetup { - - private static final Logger LOG = LogManager.getLogger(«catalog.standaloneSetupClassName».class); - «IF catalog.grammar !== null» - private static final String GRAMMAR_NAME = "«catalog.grammar.name»"; - «ENDIF» - private static final String CATALOG_FILE_PATH = "«catalog.checkFilePath»"; - - @Override - public void doSetup() { - ICheckValidatorRegistry.INSTANCE.registerValidator(«IF catalog.grammar !== null»GRAMMAR_NAME,«ENDIF» new «catalog.validatorClassName»()); - ICheckCatalogRegistry.INSTANCE.registerCatalog(«IF catalog.grammar !== null»GRAMMAR_NAME,«ENDIF» new ModelLocation( - «catalog.standaloneSetupClassName».class.getClassLoader().getResource(CATALOG_FILE_PATH), CATALOG_FILE_PATH)); - LOG.info("Standalone setup done for «catalog.checkFilePath»"); - } + public CharSequence compileIssueCodes(final CheckCatalog catalog) { + final Iterable allIssues = generatorExtensions.checkAndImplementationIssues(catalog); + final Function1 keyFunction = (final XIssueExpression issue) -> { + return CheckGeneratorExtensions.issueCode(issue); + }; + final Function1 valueFunction = (final XIssueExpression issue) -> { + return CheckGeneratorExtensions.issueName(issue); + }; + final Map allIssueNames = IterableExtensions.toMap(allIssues, keyFunction, valueFunction); + StringConcatenation builder = new StringConcatenation(); + if (!StringExtensions.isNullOrEmpty(catalog.getPackageName())) { + builder.append("package "); + builder.append(catalog.getPackageName()); + builder.append(";"); + builder.newLineIfNotEmpty(); + } + builder.newLine(); + builder.append("/**"); + builder.newLine(); + builder.append(" "); + builder.append("* Issue codes which may be used to address validation issues (for instance in quickfixes)."); + builder.newLine(); + builder.append(" "); + builder.append("*/"); + builder.newLine(); + builder.append("@SuppressWarnings(\"all\")"); + builder.newLine(); + builder.append("public final class "); + builder.append(CheckGeneratorNaming.issueCodesClassName(catalog)); + builder.append(" {"); + builder.newLineIfNotEmpty(); + builder.newLine(); + final List sortedCodes = IterableExtensions.sort(allIssueNames.keySet()); + for (final String issueCode : sortedCodes) { + builder.append(" "); + builder.append("public static final String "); + builder.append(issueCode, " "); + builder.append(" = \""); + builder.append(CheckGeneratorExtensions.issueCodeValue(catalog, allIssueNames.get(issueCode)), " "); + builder.append("\";"); + builder.newLineIfNotEmpty(); + } + builder.newLine(); + builder.append(" "); + builder.append("private "); + builder.append(CheckGeneratorNaming.issueCodesClassName(catalog), " "); + builder.append("() {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("// Prevent instantiation."); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append("}"); + builder.newLine(); + return builder; + } - @Override - public String toString() { - return "CheckValidatorSetup(«catalog.eResource.URI.path»)"; - } + /** + * Generates the Java standalone setup class which will be called by the ServiceRegistry. + * + * @param catalog + * the check catalog + * @return the standalone setup class source + */ + public CharSequence compileStandaloneSetup(final CheckCatalog catalog) { + StringConcatenation builder = new StringConcatenation(); + if (!StringExtensions.isNullOrEmpty(catalog.getPackageName())) { + builder.append("package "); + builder.append(catalog.getPackageName()); + builder.append(";"); + builder.newLineIfNotEmpty(); + } + builder.newLine(); + builder.append("import org.apache.logging.log4j.Logger;"); + builder.newLine(); + builder.append("import org.apache.logging.log4j.LogManager;"); + builder.newLine(); + builder.newLine(); + builder.append("import com.avaloq.tools.ddk.check.runtime.configuration.ModelLocation;"); + builder.newLine(); + builder.append("import com.avaloq.tools.ddk.check.runtime.registry.ICheckCatalogRegistry;"); + builder.newLine(); + builder.append("import com.avaloq.tools.ddk.check.runtime.registry.ICheckValidatorRegistry;"); + builder.newLine(); + builder.append("import com.avaloq.tools.ddk.check.runtime.registry.ICheckValidatorStandaloneSetup;"); + builder.newLine(); + builder.newLine(); + builder.append("/**"); + builder.newLine(); + builder.append(" "); + builder.append("* Standalone setup for "); + builder.append(catalog.getName(), " "); + builder.append(" as required by the standalone builder."); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("*/"); + builder.newLine(); + builder.append("@SuppressWarnings(\"nls\")"); + builder.newLine(); + builder.append("public class "); + builder.append(checkGeneratorNaming.standaloneSetupClassName(catalog)); + builder.append(" implements ICheckValidatorStandaloneSetup {"); + builder.newLineIfNotEmpty(); + builder.newLine(); + builder.append(" "); + builder.append("private static final Logger LOG = LogManager.getLogger("); + builder.append(checkGeneratorNaming.standaloneSetupClassName(catalog), " "); + builder.append(".class);"); + builder.newLineIfNotEmpty(); + if (catalog.getGrammar() != null) { + builder.append(" "); + builder.append("private static final String GRAMMAR_NAME = \""); + builder.append(catalog.getGrammar().getName(), " "); + builder.append("\";"); + builder.newLineIfNotEmpty(); + } + builder.append(" "); + builder.append("private static final String CATALOG_FILE_PATH = \""); + builder.append(checkGeneratorNaming.checkFilePath(catalog), " "); + builder.append("\";"); + builder.newLineIfNotEmpty(); + builder.newLine(); + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("public void doSetup() {"); + builder.newLine(); + builder.append(" "); + builder.append("ICheckValidatorRegistry.INSTANCE.registerValidator("); + if (catalog.getGrammar() != null) { + builder.append("GRAMMAR_NAME,"); } - ''' + builder.append(" new "); + builder.append(checkGeneratorNaming.validatorClassName(catalog), " "); + builder.append("());"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("ICheckCatalogRegistry.INSTANCE.registerCatalog("); + if (catalog.getGrammar() != null) { + builder.append("GRAMMAR_NAME,"); + } + builder.append(" new ModelLocation("); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(checkGeneratorNaming.standaloneSetupClassName(catalog), " "); + builder.append(".class.getClassLoader().getResource(CATALOG_FILE_PATH), CATALOG_FILE_PATH));"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("LOG.info(\"Standalone setup done for "); + builder.append(checkGeneratorNaming.checkFilePath(catalog), " "); + builder.append("\");"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("public String toString() {"); + builder.newLine(); + builder.append(" "); + builder.append("return \"CheckValidatorSetup("); + builder.append(catalog.eResource().getURI().path(), " "); + builder.append(")\";"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append("}"); + builder.newLine(); + return builder; } - /* + /** * Writes contents of the service registry file containing fully qualified class names of all validators. * See also http://docs.oracle.com/javase/1.4.2/docs/api/javax/imageio/spi/ServiceRegistry.html + * + * @param catalog + * the check catalog + * @param serviceRegistryFileName + * the service registry file name + * @param fsa + * the file system access + * @return the service registry contents */ - def generateServiceRegistry(CheckCatalog catalog, String serviceRegistryFileName, IFileSystemAccess fsa) { - val config = (fsa as AbstractFileSystemAccess).outputConfigurations.get(CheckGeneratorConstants::CHECK_REGISTRY_OUTPUT) - val path = config.outputDirectory + "/" + serviceRegistryFileName - val contents = catalog.getContents(path) - contents.add(catalog.qualifiedStandaloneSetupClassName) - ''' - «FOR c:contents» - «c» - «ENDFOR» - ''' + public CharSequence generateServiceRegistry(final CheckCatalog catalog, final String serviceRegistryFileName, final IFileSystemAccess fsa) { + final OutputConfiguration config = ((AbstractFileSystemAccess) fsa).getOutputConfigurations().get(CheckGeneratorConstants.CHECK_REGISTRY_OUTPUT); + final String path = config.getOutputDirectory() + "/" + serviceRegistryFileName; + final Set contents = generatorExtensions.getContents(catalog, path); + contents.add(checkGeneratorNaming.qualifiedStandaloneSetupClassName(catalog)); + StringConcatenation builder = new StringConcatenation(); + for (final String c : contents) { + builder.append(c); + builder.newLineIfNotEmpty(); + } + return builder; } - override ITreeAppendable _generateMember(JvmField field, ITreeAppendable appendable, GeneratorConfig config) { + // CHECKSTYLE:CONSTANTS-ON + + @Override + public ITreeAppendable _generateMember(final JvmField field, final ITreeAppendable appendable, final GeneratorConfig config) { // Suppress generation of the "artificial" fields for FormalParameters in check impls, but not elsewhere. - if (field.final && !field.static) { // A bit hacky to use this as the distinction... - val FormalParameter parameter = compiler.getFormalParameter(field); - if (parameter !== null) { + if (field.isFinal() && !field.isStatic()) { // A bit hacky to use this as the distinction... + final FormalParameter parameter = compiler.getFormalParameter(field); + if (parameter != null) { return appendable; } } return super._generateMember(field, appendable, config); } } - diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorExtensions.java b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorExtensions.java index e38d14a605..531d54f3c1 100644 --- a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorExtensions.java +++ b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorExtensions.java @@ -8,260 +8,515 @@ * Contributors: * Avaloq Group AG - initial API and implementation *******************************************************************************/ -package com.avaloq.tools.ddk.check.generator - -import com.avaloq.tools.ddk.check.check.Check -import com.avaloq.tools.ddk.check.check.CheckCatalog -import com.avaloq.tools.ddk.check.check.Context -import com.avaloq.tools.ddk.check.check.Implementation -import com.avaloq.tools.ddk.check.check.TriggerKind -import com.avaloq.tools.ddk.check.check.XIssueExpression -import com.google.common.collect.Iterables -import com.google.common.collect.Sets -import com.google.common.io.CharStreams -import java.io.InputStreamReader -import java.io.StringReader -import java.util.Set -import java.util.regex.Pattern -import org.eclipse.core.resources.IFile -import org.eclipse.core.resources.IProject -import org.eclipse.core.resources.ResourcesPlugin -import org.eclipse.core.runtime.Path -import org.eclipse.emf.ecore.EObject -import org.eclipse.jdt.internal.ui.text.javadoc.JavaDoc2HTMLTextReader -import org.eclipse.xtext.EcoreUtil2 -import org.eclipse.xtext.validation.CheckType - -import static extension com.avaloq.tools.ddk.check.generator.CheckGeneratorNaming.* -import static extension com.avaloq.tools.ddk.check.util.CheckUtil.* - -class CheckGeneratorExtensions { - - def dispatch String qualifiedIssueCodeName(XIssueExpression issue) { - val result = issue.issueCode() - if (result === null) { - null +package com.avaloq.tools.ddk.check.generator; + +import java.io.InputStreamReader; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Path; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.jdt.internal.ui.text.javadoc.JavaDoc2HTMLTextReader; +import org.eclipse.xtext.EcoreUtil2; +import org.eclipse.xtext.validation.CheckType; +import org.eclipse.xtext.xbase.lib.ListExtensions; + +import com.avaloq.tools.ddk.check.check.Check; +import com.avaloq.tools.ddk.check.check.CheckCatalog; +import com.avaloq.tools.ddk.check.check.Context; +import com.avaloq.tools.ddk.check.check.Implementation; +import com.avaloq.tools.ddk.check.check.TriggerKind; +import com.avaloq.tools.ddk.check.check.XIssueExpression; +import com.avaloq.tools.ddk.check.util.CheckUtil; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; +import com.google.common.io.CharStreams; + +import static com.avaloq.tools.ddk.check.generator.CheckGeneratorNaming.issueCodesClassName; +import static com.avaloq.tools.ddk.check.generator.CheckGeneratorNaming.parent; + + +/** + * Extension methods used by the Check generator. + */ +@SuppressWarnings({"checkstyle:MethodName", "nls"}) +public class CheckGeneratorExtensions { + + /** + * Returns the qualified Java name for an issue code. + * + * @param issue + * the issue expression + * @return the qualified issue code name, or {@code null} if the issue code is null + */ + protected String _qualifiedIssueCodeName(final XIssueExpression issue) { + final String result = issueCode(issue); + if (result == null) { + return null; } else { - issue.parent(typeof(CheckCatalog)).issueCodesClassName + '.' + result + return issueCodesClassName(parent(issue, CheckCatalog.class)) + "." + result; } } - /* Returns the qualified Java name for an issue code. */ - def dispatch String qualifiedIssueCodeName(Context context) { - context.parent(typeof(CheckCatalog)).issueCodesClassName + '.' + context.issueCode + /** + * Returns the qualified Java name for an issue code. + * + * @param context + * the context + * @return the qualified issue code name + */ + protected String _qualifiedIssueCodeName(final Context context) { + return issueCodesClassName(parent(context, CheckCatalog.class)) + "." + issueCode(context); } - /* Gets the simple issue code name for a check. */ - def static dispatch String issueCode(Check check) { - if (null !== check.name) { - check.name.splitCamelCase.toUpperCase + /** + * Gets the simple issue code name for a check. + * + * @param check + * the check + * @return the issue code string + */ + protected static String _issueCode(final Check check) { + if (null != check.getName()) { + return splitCamelCase(check.getName()).toUpperCase(); } else { - "ERROR_ISSUE_CODE_NAME_CHECK" // should only happen if the ID is missing, which will fail a validation + return "ERROR_ISSUE_CODE_NAME_CHECK"; // should only happen if the ID is missing, which will fail a validation } } - /* Gets the simple issue code name for an issue expression. */ - def static dispatch String issueCode(XIssueExpression issue) { - if (issue.issueCode !== null) { - issue.issueCode.splitCamelCase.toUpperCase - } else if (issue.check !== null && !issue.check.eIsProxy) { - issueCode(issue.check) - } else if (issue.parent(Check) !== null) { - issueCode(issue.parent(Check)) + /** + * Gets the simple issue code name for an issue expression. + * + * @param issue + * the issue expression + * @return the issue code string + */ + protected static String _issueCode(final XIssueExpression issue) { + if (issue.getIssueCode() != null) { + return splitCamelCase(issue.getIssueCode()).toUpperCase(); + } else if (issue.getCheck() != null && !issue.getCheck().eIsProxy()) { + return issueCode(issue.getCheck()); + } else if (parent(issue, Check.class) != null) { + return issueCode(parent(issue, Check.class)); } else { - "ERROR_ISSUE_CODE_NAME_XISSUEEXPRESSION" // should not happen + return "ERROR_ISSUE_CODE_NAME_XISSUEEXPRESSION"; // should not happen } } - /* Gets the simple issue code name for a check. */ - def static dispatch String issueName(Check check) { - if (null !== check.name) { - check.name + /** + * Gets the simple issue code name for a check. + * + * @param check + * the check + * @return the issue name string + */ + protected static String _issueName(final Check check) { + if (null != check.getName()) { + return check.getName(); } else { - "ErrorIssueCodeNameCheck" // should only happen if the ID is missing, which will fail a validation + return "ErrorIssueCodeNameCheck"; // should only happen if the ID is missing, which will fail a validation } } - /* Gets the simple issue code name for an issue expression. */ - def static dispatch String issueName(XIssueExpression issue) { - if (issue.issueCode !== null) { - issue.issueCode - } else if (issue.check !== null && !issue.check.eIsProxy) { - issueName(issue.check) - } else if (issue.parent(Check) !== null) { - issueName(issue.parent(Check)) + /** + * Gets the simple issue code name for an issue expression. + * + * @param issue + * the issue expression + * @return the issue name string + */ + protected static String _issueName(final XIssueExpression issue) { + if (issue.getIssueCode() != null) { + return issue.getIssueCode(); + } else if (issue.getCheck() != null && !issue.getCheck().eIsProxy()) { + return issueName(issue.getCheck()); + } else if (parent(issue, Check.class) != null) { + return issueName(parent(issue, Check.class)); } else { - "ErrorIssueCodeName_XIssueExpresion" // should not happen + return "ErrorIssueCodeName_XIssueExpresion"; // should not happen } } - def static issueCodePrefix(CheckCatalog catalog) { - catalog.packageName + "." + catalog.issueCodesClassName + "." + /** + * Returns the issue code prefix for a catalog. + * + * @param catalog + * the check catalog + * @return the issue code prefix + */ + public static String issueCodePrefix(final CheckCatalog catalog) { + return catalog.getPackageName() + "." + issueCodesClassName(catalog) + "."; } - /* Returns the value of an issue code. */ - def static issueCodeValue(EObject object, String issueName) { - val catalog = object.parent(typeof(CheckCatalog)) - catalog.issueCodePrefix + issueName.splitCamelCase.toIssueCodeName + /** + * Returns the value of an issue code. + * + * @param object + * the EObject context + * @param issueName + * the issue name + * @return the issue code value + */ + public static String issueCodeValue(final EObject object, final String issueName) { + final CheckCatalog catalog = parent(object, CheckCatalog.class); + return issueCodePrefix(catalog) + CheckUtil.toIssueCodeName(splitCamelCase(issueName)); } - /* Gets the issue label for a Check. */ - def dispatch String issueLabel(Check check) { - check.label + /** + * Gets the issue label for a Check. + * + * @param check + * the check + * @return the label + */ + protected String _issueLabel(final Check check) { + return check.getLabel(); } - /* Gets the issue label for an issue expression. */ - def dispatch String issueLabel(XIssueExpression issue) { - if (issue.check !== null && !issue.check.eIsProxy) { - issueLabel(issue.check) - } else if (issue.parent(Check) !== null) { - issueLabel(issue.parent(Check)) + /** + * Gets the issue label for an issue expression. + * + * @param issue + * the issue expression + * @return the label + */ + protected String _issueLabel(final XIssueExpression issue) { + if (issue.getCheck() != null && !issue.getCheck().eIsProxy()) { + return issueLabel(issue.getCheck()); + } else if (parent(issue, Check.class) != null) { + return issueLabel(parent(issue, Check.class)); } else { - "ERROR_ISSUE_LABEL_XISSUEEXPRESSION" // should not happen + return "ERROR_ISSUE_LABEL_XISSUEEXPRESSION"; // should not happen } } - /* Converts a string such as "AbcDef" to "ABC_DEF". */ - def static String splitCamelCase(String string) { - string.replaceAll( - String::format( - "%s|%s|%s", + /** + * Converts a string such as "AbcDef" to "ABC_DEF". + * + * @param string + * the string to convert + * @return the converted string + */ + public static String splitCamelCase(final String string) { + return string.replaceAll( + "%s|%s|%s".formatted( "(?<=[A-Z])(?=[A-Z][a-z])", "(?<=[^A-Z_])(?=[A-Z])", - "(?<=[A-Za-z])(?=[^A-Za-z_])" - ), - "_" - ) + "(?<=[A-Za-z])(?=[^A-Za-z_])"), + "_"); } - def CheckType checkType(Check check) { + /** + * Returns the CheckType for a check. + * + * @param check + * the check + * @return the check type + */ + public CheckType checkType(final Check check) { /* TODO handle the case of independent check implementations * An Implementation is not a Check and has no kind, * but it may execute checks of various types. * As it is we treat them all as FAST regardless of declared kind. */ - val TriggerKind kind = check?.kind ?: TriggerKind::FAST; + TriggerKind kind = check != null ? check.getKind() : null; + if (kind == null) { + kind = TriggerKind.FAST; + } return switch (kind) { - case TriggerKind::EXPENSIVE: CheckType::EXPENSIVE - case TriggerKind::NORMAL: CheckType::NORMAL - case TriggerKind::FAST: CheckType::FAST + case EXPENSIVE -> CheckType.EXPENSIVE; + case NORMAL -> CheckType.NORMAL; + case FAST -> CheckType.FAST; }; } - /* Returns a default CheckType for a non-Check context. */ - def CheckType checkType(Context context) { - val container = context.eContainer(); - val Check check = if (container instanceof Check) container else null; + /** + * Returns a default CheckType for a non-Check context. + * + * @param context + * the context + * @return the check type + */ + public CheckType checkType(final Context context) { + final EObject container = context.eContainer(); + final Check check = container instanceof Check ? (Check) container : null; return checkType(check); } - def String checkTypeQName(Context context) { + /** + * Returns the qualified CheckType name for a context. + * + * @param context + * the context + * @return the qualified check type name + */ + public String checkTypeQName(final Context context) { return "CheckType." + checkType(context); } - def issues(EObject object) { - EcoreUtil2::eAllContents(object).filter(typeof(XIssueExpression)) + /** + * Returns all issue expressions contained in an EObject. + * + * @param object + * the object to search + * @return the issue expressions + */ + public Iterable issues(final EObject object) { + return Iterables.filter(EcoreUtil2.eAllContents(object), XIssueExpression.class); } - def issues(CheckCatalog catalog) { - catalog.allChecks.map(check|check.issues).flatten + /** + * Returns all issue expressions for all checks in a catalog. + * + * @param catalog + * the check catalog + * @return the issue expressions + */ + public Iterable issues(final CheckCatalog catalog) { + return Iterables.concat(ListExtensions.map(catalog.getAllChecks(), check -> issues(check))); } - def issues(Implementation implementation) { - implementation.context.issues + /** + * Returns all issue expressions for an implementation. + * + * @param implementation + * the implementation + * @return the issue expressions + */ + public Iterable issues(final Implementation implementation) { + return issues(implementation.getContext()); } - /* Returns all Check and Implementation Issues for a CheckCatalog. Issues are not necessarily unique. */ - def checkAndImplementationIssues(CheckCatalog catalog) { - val checkIssues = catalog.issues // Issues for all Checks - val implIssues = catalog.implementations.map(impl|impl.issues).flatten // Issues for all Implementations - return Iterables::concat(checkIssues, implIssues) // all Issue instances + /** + * Returns all Check and Implementation Issues for a CheckCatalog. Issues are not necessarily unique. + * + * @param catalog + * the check catalog + * @return all issue expressions + */ + public Iterable checkAndImplementationIssues(final CheckCatalog catalog) { + final Iterable checkIssues = issues(catalog); // Issues for all Checks + final Iterable implIssues = Iterables.concat(ListExtensions.map(catalog.getImplementations(), impl -> issues(impl))); // Issues for all Implementations + return Iterables.concat(checkIssues, implIssues); // all Issue instances } - def issuedCheck(XIssueExpression expression) { - if (expression.check !== null) { - expression.check + /** + * Returns the check associated with an issue expression. + * + * @param expression + * the issue expression + * @return the associated check, or {@code null} + */ + public Check issuedCheck(final XIssueExpression expression) { + if (expression.getCheck() != null) { + return expression.getCheck(); } else { - val containerCheck = EcoreUtil2::getContainerOfType(expression, typeof(Check)) - if (containerCheck !== null) { - containerCheck + final Check containerCheck = EcoreUtil2.getContainerOfType(expression, Check.class); + if (containerCheck != null) { + return containerCheck; //TODO we obviously need a validation in the language so that there is always a value here! } + return null; } } /** * Gets the IFile which is associated with given object's eResource, or null if none * could be determined. + * + * @param object + * the EObject + * @return the associated file, or {@code null} */ - def IFile fileForObject(EObject object) { - val res = object.eResource - if (res.URI.platform) { - return ResourcesPlugin::workspace.root.findMember(res.URI.toPlatformString(true)) as IFile + public IFile fileForObject(final EObject object) { + final Resource res = object.eResource(); + if (res.getURI().isPlatform()) { + return (IFile) ResourcesPlugin.getWorkspace().getRoot().findMember(res.getURI().toPlatformString(true)); } - return null + return null; } /** * Gets the IProject which is associated with a given EObject or null * if none could be determined. + * + * @param object + * the EObject + * @return the associated project, or {@code null} */ - def IProject projectForObject(EObject object) { - return object?.fileForObject?.project + public IProject projectForObject(final EObject object) { + final IFile file = object != null ? fileForObject(object) : null; + return file != null ? file.getProject() : null; } /** * Gets the name of the project in which given object is contained. + * + * @param object + * the EObject + * @return the bundle name, or {@code null} */ - def String bundleName(EObject object) { - val proj = object.projectForObject - if (proj !== null) { - return proj.name + public String bundleName(final EObject object) { + final IProject proj = projectForObject(object); + if (proj != null) { + return proj.getName(); } - return null + return null; } - /* - * Replace binding placeholders of a message with "...". + /** + * Replace binding placeholders of a message with "...". + * + * @param message + * the message + * @return the message with placeholders replaced */ - def String replacePlaceholder(String message) { - val p = Pattern::compile("\\{[0-9]+\\}") - val m = p.matcher(message) - m.replaceAll("...") + public String replacePlaceholder(final String message) { + final Pattern p = Pattern.compile("\\{[0-9]+\\}"); + final Matcher m = p.matcher(message); + return m.replaceAll("..."); } - /* - * Format the Check description for Eclipse Help + /** + * Format the Check description for Eclipse Help. + * + * @param comment + * the comment to format + * @return the formatted HTML, or {@code null} */ - def String formatDescription(String comment) { - if (comment === null) { - return null + // CHECKSTYLE:CHECK-OFF IllegalCatch + public String formatDescription(final String comment) { + if (comment == null) { + return null; } try { - val reader = new JavaDoc2HTMLTextReader(new StringReader(comment)) - return reader.string - } catch (Exception e) { - return null + final JavaDoc2HTMLTextReader reader = new JavaDoc2HTMLTextReader(new StringReader(comment)); + return reader.getString(); + } catch (final Exception e) { + return null; } } + // CHECKSTYLE:CHECK-ON IllegalCatch - def Set getContents(CheckCatalog catalog, String path) { - val project = catalog.projectForObject - if (project !== null) { // In some compiler tests we may not have a project. - val file = project.getFile(new Path(path)) - if (file.exists) { - val reader = new InputStreamReader(file.getContents()) - try { - val content = CharStreams::readLines(reader) - return Sets.newTreeSet(content) - } finally { - reader.close + /** + * Gets the contents of a file in the project. + * + * @param catalog + * the check catalog + * @param path + * the file path + * @return the set of lines + * @throws IllegalStateException + * if the file cannot be read + */ + // CHECKSTYLE:CHECK-OFF IllegalCatch + public Set getContents(final CheckCatalog catalog, final String path) { + final IProject project = projectForObject(catalog); + if (project != null) { // In some compiler tests we may not have a project. + final IFile file = project.getFile(new Path(path)); + if (file.exists()) { + try (InputStreamReader reader = new InputStreamReader(file.getContents(), StandardCharsets.UTF_8)) { + final List content = CharStreams.readLines(reader); + return Sets.newTreeSet(content); + } catch (final RuntimeException e) { + throw e; + } catch (final Exception e) { + throw new IllegalStateException(e); } } } - newLinkedHashSet() + return new LinkedHashSet<>(); } + // CHECKSTYLE:CHECK-ON IllegalCatch -} + /** + * Returns the qualified issue code name for an EObject. + * + * @param context + * the EObject context + * @return the qualified issue code name + * @throws IllegalArgumentException + * if the parameter type is not handled + */ + public String qualifiedIssueCodeName(final EObject context) { + if (context instanceof Context) { + return _qualifiedIssueCodeName((Context) context); + } else if (context instanceof XIssueExpression) { + return _qualifiedIssueCodeName((XIssueExpression) context); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + + Arrays.asList(context).toString()); + } + } + /** + * Returns the issue code for an EObject. + * + * @param check + * the EObject (Check or XIssueExpression) + * @return the issue code string + * @throws IllegalArgumentException + * if the parameter type is not handled + */ + public static String issueCode(final EObject check) { + if (check instanceof Check) { + return _issueCode((Check) check); + } else if (check instanceof XIssueExpression) { + return _issueCode((XIssueExpression) check); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + + Arrays.asList(check).toString()); + } + } + + /** + * Returns the issue name for an EObject. + * + * @param check + * the EObject (Check or XIssueExpression) + * @return the issue name string + * @throws IllegalArgumentException + * if the parameter type is not handled + */ + public static String issueName(final EObject check) { + if (check instanceof Check) { + return _issueName((Check) check); + } else if (check instanceof XIssueExpression) { + return _issueName((XIssueExpression) check); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + + Arrays.asList(check).toString()); + } + } + + /** + * Returns the issue label for an EObject. + * + * @param check + * the EObject (Check or XIssueExpression) + * @return the label string + * @throws IllegalArgumentException + * if the parameter type is not handled + */ + public String issueLabel(final EObject check) { + if (check instanceof Check) { + return _issueLabel((Check) check); + } else if (check instanceof XIssueExpression) { + return _issueLabel((XIssueExpression) check); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + + Arrays.asList(check).toString()); + } + } +} diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorNaming.java b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorNaming.java index b8131e33cf..49f1c04f16 100644 --- a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorNaming.java +++ b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGeneratorNaming.java @@ -8,167 +8,333 @@ * Contributors: * Avaloq Group AG - initial API and implementation *******************************************************************************/ -package com.avaloq.tools.ddk.check.generator +package com.avaloq.tools.ddk.check.generator; -import com.avaloq.tools.ddk.check.check.Category -import com.avaloq.tools.ddk.check.check.Check -import com.avaloq.tools.ddk.check.check.CheckCatalog -import com.avaloq.tools.ddk.check.check.FormalParameter -import com.google.inject.Inject -import org.eclipse.emf.ecore.EObject -import org.eclipse.xtext.EcoreUtil2 -import org.eclipse.xtext.naming.IQualifiedNameProvider -import org.eclipse.xtext.naming.QualifiedName +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.EcoreUtil2; +import org.eclipse.xtext.naming.IQualifiedNameProvider; +import org.eclipse.xtext.naming.QualifiedName; +import org.eclipse.xtext.xbase.lib.StringExtensions; -import static com.avaloq.tools.ddk.check.runtime.CheckRuntimeConstants.ISSUE_CODES_CLASS_NAME_SUFFIX +import com.avaloq.tools.ddk.check.check.Category; +import com.avaloq.tools.ddk.check.check.Check; +import com.avaloq.tools.ddk.check.check.CheckCatalog; +import com.avaloq.tools.ddk.check.check.FormalParameter; +import com.avaloq.tools.ddk.check.runtime.CheckRuntimeConstants; +import com.google.inject.Inject; -class CheckGeneratorNaming { - @Inject IQualifiedNameProvider nameProvider +@SuppressWarnings("nls") +public class CheckGeneratorNaming { - def static T parent(EObject object, Class c) { - EcoreUtil2::getContainerOfType(object, c) + @Inject + private IQualifiedNameProvider nameProvider; + + public static T parent(final EObject object, final Class c) { + return EcoreUtil2.getContainerOfType(object, c); } - // creates a pathName out of a qualified javaPackagename - def String asPath(String javaPackageName) { - if (javaPackageName !== null) javaPackageName.replace('.', '/') + "/" else "" + /** + * creates a pathName out of a qualified javaPackagename. + * + * @param javaPackageName + * the qualified java package name + * @return the path form of the package name, or an empty string if {@code null} + */ + public String asPath(final String javaPackageName) { + if (javaPackageName != null) { + return javaPackageName.replace(".", "/") + "/"; + } else { + return ""; + } } - /* Gets the class name of the check validator. */ - def String validatorClassName(CheckCatalog c) { - c.name + "CheckImpl" + /** + * Gets the class name of the check validator. + * + * @param c + * the check catalog + * @return the validator class name + */ + public String validatorClassName(final CheckCatalog c) { + return c.getName() + "CheckImpl"; } - /* Gets the fully qualified class name of the check validator. */ - def String qualifiedValidatorClassName(CheckCatalog c) { - c.packageName + '.' + c.validatorClassName + /** + * Gets the fully qualified class name of the check validator. + * + * @param c + * the check catalog + * @return the qualified validator class name + */ + public String qualifiedValidatorClassName(final CheckCatalog c) { + return c.getPackageName() + "." + validatorClassName(c); } - /* Gets the file path of the check validator. */ - def String validatorFilePath(CheckCatalog c) { - c.packageName.asPath + c.validatorClassName + ".java" + /** + * Gets the file path of the check validator. + * + * @param c + * the check catalog + * @return the validator file path + */ + public String validatorFilePath(final CheckCatalog c) { + return asPath(c.getPackageName()) + validatorClassName(c) + ".java"; } - /* Gets the check catalog class name. */ - def String catalogClassName(CheckCatalog c) { - c.name + "CheckCatalog" + /** + * Gets the check catalog class name. + * + * @param c + * the check catalog + * @return the catalog class name + */ + public String catalogClassName(final CheckCatalog c) { + return c.getName() + "CheckCatalog"; } - /* Gets the qualified check catalog class name. */ - def String qualifiedCatalogClassName(CheckCatalog c) { - c.packageName + '.' + c.catalogClassName + /** + * Gets the qualified check catalog class name. + * + * @param c + * the check catalog + * @return the qualified catalog class name + */ + public String qualifiedCatalogClassName(final CheckCatalog c) { + return c.getPackageName() + "." + catalogClassName(c); } - /* Gets the preference initializer class name. */ - def String preferenceInitializerClassName(CheckCatalog c) { - c.name + "PreferenceInitializer" + /** + * Gets the preference initializer class name. + * + * @param c + * the check catalog + * @return the preference initializer class name + */ + public String preferenceInitializerClassName(final CheckCatalog c) { + return c.getName() + "PreferenceInitializer"; } - /* Gets the qualified standalone setup class name. */ - def String qualifiedStandaloneSetupClassName(CheckCatalog c) { - c.packageName + '.' + c.standaloneSetupClassName + /** + * Gets the qualified standalone setup class name. + * + * @param c + * the check catalog + * @return the qualified standalone setup class name + */ + public String qualifiedStandaloneSetupClassName(final CheckCatalog c) { + return c.getPackageName() + "." + standaloneSetupClassName(c); } - /* Gets the standalone setup class name. */ - def String standaloneSetupClassName(CheckCatalog c) { - c.name + "StandaloneSetup" + /** + * Gets the standalone setup class name. + * + * @param c + * the check catalog + * @return the standalone setup class name + */ + public String standaloneSetupClassName(final CheckCatalog c) { + return c.getName() + "StandaloneSetup"; } - /* Gets the qualified preference initializer class name. */ - def String qualifiedPreferenceInitializerClassName(CheckCatalog c) { - c.packageName + '.' + c.preferenceInitializerClassName + /** + * Gets the qualified preference initializer class name. + * + * @param c + * the check catalog + * @return the qualified preference initializer class name + */ + public String qualifiedPreferenceInitializerClassName(final CheckCatalog c) { + return c.getPackageName() + "." + preferenceInitializerClassName(c); } - /* Gets the standalone setup class file path. */ - def String standaloneSetupPath(CheckCatalog c) { - c.packageName.asPath + c.standaloneSetupClassName + ".java" + /** + * Gets the standalone setup class file path. + * + * @param c + * the check catalog + * @return the standalone setup file path + */ + public String standaloneSetupPath(final CheckCatalog c) { + return asPath(c.getPackageName()) + standaloneSetupClassName(c) + ".java"; } - /* Gets the documentation file name. */ - def String docFileName(CheckCatalog c){ - c.name + ".html" + /** + * Gets the documentation file name. + * + * @param c + * the check catalog + * @return the documentation file name + */ + public String docFileName(final CheckCatalog c) { + return c.getName() + ".html"; } - /* Gets the issue codes class name. */ - def static String issueCodesClassName(CheckCatalog c) { - c.name + ISSUE_CODES_CLASS_NAME_SUFFIX + /** + * Gets the issue codes class name. + * + * @param c + * the check catalog + * @return the issue codes class name + */ + public static String issueCodesClassName(final CheckCatalog c) { + return c.getName() + CheckRuntimeConstants.ISSUE_CODES_CLASS_NAME_SUFFIX; } - /* Gets the issue codes file path. */ - def String issueCodesFilePath(CheckCatalog c) { - c.packageName.asPath + c.issueCodesClassName + ".java" + /** + * Gets the issue codes file path. + * + * @param c + * the check catalog + * @return the issue codes file path + */ + public String issueCodesFilePath(final CheckCatalog c) { + return asPath(c.getPackageName()) + issueCodesClassName(c) + ".java"; } - /* Gets the quickfix provider class name. */ - def String quickfixClassName(CheckCatalog c) { - c.name + "QuickfixProvider" + /** + * Gets the quickfix provider class name. + * + * @param c + * the check catalog + * @return the quickfix provider class name + */ + public String quickfixClassName(final CheckCatalog c) { + return c.getName() + "QuickfixProvider"; } - /* Gets the qualified quickfix provider class name. */ - def String qualifiedQuickfixClassName(CheckCatalog c) { - c.packageName + '.' + c.quickfixClassName + /** + * Gets the qualified quickfix provider class name. + * + * @param c + * the check catalog + * @return the qualified quickfix provider class name + */ + public String qualifiedQuickfixClassName(final CheckCatalog c) { + return c.getPackageName() + "." + quickfixClassName(c); } - /* Gets the quickfix provider file path. */ - def String quickfixFilePath(CheckCatalog c) { - c.packageName.asPath + c.quickfixClassName + ".java" + /** + * Gets the quickfix provider file path. + * + * @param c + * the check catalog + * @return the quickfix provider file path + */ + public String quickfixFilePath(final CheckCatalog c) { + return asPath(c.getPackageName()) + quickfixClassName(c) + ".java"; } - /* Gets the full path to the check file, e.g. com/avaloq/MyChecks.check. */ - def String checkFilePath(CheckCatalog c) { - c.packageName.asPath + c.name + ".check" + /** + * Gets the full path to the check file, e.g. com/avaloq/MyChecks.check. + * + * @param c + * the check catalog + * @return the check file path + */ + public String checkFilePath(final CheckCatalog c) { + return asPath(c.getPackageName()) + c.getName() + ".check"; } - /* Gets the name of the getter method generated for a formal parameter. */ - def String formalParameterGetterName(FormalParameter p) { - val check = p.eContainer as Check + /** + * Gets the name of the getter method generated for a formal parameter. + * + * @param p + * the formal parameter + * @return the getter method name + */ + public String formalParameterGetterName(final FormalParameter p) { + final Check check = (Check) p.eContainer(); return "get" - + check.name.toFirstUpper + + StringExtensions.toFirstUpper(check.getName()) + "_" - + p.name.toFirstUpper + + StringExtensions.toFirstUpper(p.getName()); } - /* Gets the name of the getter method generated for a field. */ - def String fieldGetterName(String fieldName) { - "get" + fieldName.toFirstUpper + /** + * Gets the name of the getter method generated for a field. + * + * @param fieldName + * the field name + * @return the getter method name + */ + public String fieldGetterName(final String fieldName) { + return "get" + StringExtensions.toFirstUpper(fieldName); } - /* Check catalog instance name in the validator */ - def String catalogInstanceName(EObject object) { - EcoreUtil2::getContainerOfType(object, typeof(CheckCatalog)).name.toFirstLower + "Catalog" + /** + * Check catalog instance name in the validator. + * + * @param object + * the object + * @return the catalog instance name + */ + public String catalogInstanceName(final EObject object) { + return StringExtensions.toFirstLower(EcoreUtil2.getContainerOfType(object, CheckCatalog.class).getName()) + "Catalog"; } - /* Check issue code to label map field name in the catalog */ - def String issueCodeToLabelMapFieldName() { - "issueCodeToLabelMap" + /** + * Check issue code to label map field name in the catalog. + * + * @return the field name + */ + public String issueCodeToLabelMapFieldName() { + return "issueCodeToLabelMap"; } - /* Gets the name of the default validator class. */ - def String defaultValidatorClassName() { - "DispatchingCheckImpl" + /** + * Gets the name of the default validator class. + * + * @return the default validator class name + */ + public String defaultValidatorClassName() { + return "DispatchingCheckImpl"; } - /* Gets the fully qualified name of the default validator class. */ - def String qualifiedDefaultValidatorClassName() { - "com.avaloq.tools.ddk.check.runtime.issue." + defaultValidatorClassName + /** + * Gets the fully qualified name of the default validator class. + * + * @return the qualified default validator class name + */ + public String qualifiedDefaultValidatorClassName() { + return "com.avaloq.tools.ddk.check.runtime.issue." + defaultValidatorClassName(); } - /* Gets the prefix for the context id (used in contexts.xml) */ - def getContextIdPrefix(QualifiedName catalog){ - catalog.lastSegment.toString.toLowerCase + "_" // TODO make context id use fully qualified catalog names + /** + * Gets the prefix for the context id (used in contexts.xml). + * + * @param catalog + * the qualified name of the catalog + * @return the context id prefix + */ + public String getContextIdPrefix(final QualifiedName catalog) { + return catalog.getLastSegment().toLowerCase() + "_"; // TODO make context id use fully qualified catalog names } - /* Gets the full context id (used in contexts.xml) */ - def String getContextId(Check check) { - val catalog = check.parent(typeof (CheckCatalog)) - nameProvider.apply(catalog).contextIdPrefix + check.label.replaceAll(" ", "").replaceAll("\"", "").replaceAll("'", "").toLowerCase + /** + * Gets the full context id (used in contexts.xml). + * + * @param check + * the check + * @return the context id + */ + public String getContextId(final Check check) { + final CheckCatalog catalog = parent(check, CheckCatalog.class); + return getContextIdPrefix(nameProvider.apply(catalog)) + + check.getLabel().replaceAll(" ", "").replaceAll("\"", "").replaceAll("'", "").toLowerCase(); } - /* Gets the full context id (used in contexts.xml) */ - def String getContextId(Category category) { - val catalog = category.parent(typeof (CheckCatalog)) - nameProvider.apply(catalog).contextIdPrefix + category.label.replaceAll(" ", "").replaceAll("\"", "").replaceAll("'", "").toLowerCase + /** + * Gets the full context id (used in contexts.xml). + * + * @param category + * the category + * @return the context id + */ + public String getContextId(final Category category) { + final CheckCatalog catalog = parent(category, CheckCatalog.class); + return getContextIdPrefix(nameProvider.apply(catalog)) + + category.getLabel().replaceAll(" ", "").replaceAll("\"", "").replaceAll("'", "").toLowerCase(); } } diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/jvmmodel/CheckJvmModelInferrer.java b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/jvmmodel/CheckJvmModelInferrer.java index f323ac1413..109cac4afe 100644 --- a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/jvmmodel/CheckJvmModelInferrer.java +++ b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/jvmmodel/CheckJvmModelInferrer.java @@ -8,65 +8,79 @@ * Contributors: * Avaloq Group AG - initial API and implementation *******************************************************************************/ -package com.avaloq.tools.ddk.check.jvmmodel - -import com.avaloq.tools.ddk.check.CheckConstants -import com.avaloq.tools.ddk.check.check.Check -import com.avaloq.tools.ddk.check.check.CheckCatalog -import com.avaloq.tools.ddk.check.check.Context -import com.avaloq.tools.ddk.check.check.FormalParameter -import com.avaloq.tools.ddk.check.check.Implementation -import com.avaloq.tools.ddk.check.generator.CheckGeneratorExtensions -import com.avaloq.tools.ddk.check.generator.CheckGeneratorNaming -import com.avaloq.tools.ddk.check.generator.CheckPropertiesGenerator -import com.avaloq.tools.ddk.check.resource.CheckLocationInFileProvider -import com.avaloq.tools.ddk.check.runtime.configuration.ICheckConfigurationStoreService -import com.avaloq.tools.ddk.check.runtime.issue.AbstractIssue -import com.avaloq.tools.ddk.check.runtime.issue.DispatchingCheckImpl -import com.avaloq.tools.ddk.check.runtime.issue.DispatchingCheckImpl.DiagnosticCollector -import com.avaloq.tools.ddk.check.runtime.issue.SeverityKind -import com.avaloq.tools.ddk.check.validation.IssueCodes -import com.google.common.collect.ImmutableMap -import com.google.common.collect.Lists -import com.google.inject.Inject -import com.google.inject.Singleton -import java.util.ArrayList -import java.util.Collections -import java.util.List -import java.util.Map -import java.util.Objects -import java.util.TreeMap -import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer -import org.eclipse.core.runtime.preferences.IEclipsePreferences -import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.EStructuralFeature -import org.eclipse.emf.ecore.resource.Resource -import org.eclipse.xtext.common.types.JvmAnnotationReference -import org.eclipse.xtext.common.types.JvmAnnotationType -import org.eclipse.xtext.common.types.JvmDeclaredType -import org.eclipse.xtext.common.types.JvmField -import org.eclipse.xtext.common.types.JvmMember -import org.eclipse.xtext.common.types.JvmOperation -import org.eclipse.xtext.common.types.JvmParameterizedTypeReference -import org.eclipse.xtext.common.types.JvmTypeReference -import org.eclipse.xtext.common.types.JvmVisibility -import org.eclipse.xtext.common.types.TypesFactory -import org.eclipse.xtext.diagnostics.Severity -import org.eclipse.xtext.util.Strings -import org.eclipse.xtext.validation.CheckMode -import org.eclipse.xtext.validation.CheckType -import org.eclipse.xtext.validation.EObjectDiagnosticImpl -import org.eclipse.xtext.xbase.XFeatureCall -import org.eclipse.xtext.xbase.XMemberFeatureCall -import org.eclipse.xtext.xbase.XbaseFactory -import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable -import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer -import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor -import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder -import org.eclipse.xtext.xbase.lib.Procedures.Procedure1 - -import static extension com.avaloq.tools.ddk.check.generator.CheckGeneratorExtensions.* -import static extension org.apache.commons.text.StringEscapeUtils.escapeJava +package com.avaloq.tools.ddk.check.jvmmodel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.apache.commons.text.StringEscapeUtils; +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.xtend2.lib.StringConcatenation; +import org.eclipse.xtext.common.types.JvmAnnotationReference; +import org.eclipse.xtext.common.types.JvmAnnotationType; +import org.eclipse.xtext.common.types.JvmDeclaredType; +import org.eclipse.xtext.common.types.JvmField; +import org.eclipse.xtext.common.types.JvmGenericType; +import org.eclipse.xtext.common.types.JvmMember; +import org.eclipse.xtext.common.types.JvmOperation; +import org.eclipse.xtext.common.types.JvmParameterizedTypeReference; +import org.eclipse.xtext.common.types.JvmTypeReference; +import org.eclipse.xtext.common.types.JvmVisibility; +import org.eclipse.xtext.common.types.TypesFactory; +import org.eclipse.xtext.diagnostics.Severity; +import org.eclipse.xtext.util.Strings; +import org.eclipse.xtext.validation.CheckMode; +import org.eclipse.xtext.validation.CheckType; +import org.eclipse.xtext.validation.EObjectDiagnosticImpl; +import org.eclipse.xtext.xbase.XFeatureCall; +import org.eclipse.xtext.xbase.XMemberFeatureCall; +import org.eclipse.xtext.xbase.XbaseFactory; +import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable; +import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer; +import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor; +import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder; +import org.eclipse.xtext.xbase.lib.IterableExtensions; +import org.eclipse.xtext.xbase.lib.ListExtensions; +import org.eclipse.xtext.xbase.lib.Procedures.Procedure1; +import org.eclipse.xtext.xbase.lib.StringExtensions; + +import com.avaloq.tools.ddk.check.CheckConstants; +import com.avaloq.tools.ddk.check.check.Category; +import com.avaloq.tools.ddk.check.check.Check; +import com.avaloq.tools.ddk.check.check.CheckCatalog; +import com.avaloq.tools.ddk.check.check.Context; +import com.avaloq.tools.ddk.check.check.FormalParameter; +import com.avaloq.tools.ddk.check.check.Implementation; +import com.avaloq.tools.ddk.check.check.Member; +import com.avaloq.tools.ddk.check.check.XIssueExpression; +import com.avaloq.tools.ddk.check.generator.CheckGeneratorExtensions; +import com.avaloq.tools.ddk.check.generator.CheckGeneratorNaming; +import com.avaloq.tools.ddk.check.generator.CheckPropertiesGenerator; +import com.avaloq.tools.ddk.check.resource.CheckLocationInFileProvider; +import com.avaloq.tools.ddk.check.runtime.configuration.ICheckConfigurationStoreService; +import com.avaloq.tools.ddk.check.runtime.issue.AbstractIssue; +import com.avaloq.tools.ddk.check.runtime.issue.DispatchingCheckImpl; +import com.avaloq.tools.ddk.check.runtime.issue.DispatchingCheckImpl.DiagnosticCollector; +import com.avaloq.tools.ddk.check.runtime.issue.SeverityKind; +import com.avaloq.tools.ddk.check.validation.IssueCodes; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.inject.Inject; +import com.google.inject.Singleton; + /** *

Infers a JVM model from the source model.

@@ -74,255 +88,330 @@ *

The JVM model should contain all elements that would appear in the Java code * which is generated from the source model. Other models link against the JVM model rather than the source model.

*/ -class CheckJvmModelInferrer extends AbstractModelInferrer { +@SuppressWarnings({"checkstyle:MethodName", "nls"}) +public class CheckJvmModelInferrer extends AbstractModelInferrer { + + @Inject + private TypesFactory typesFactory; + + @Inject + private CheckLocationInFileProvider locationInFileProvider; + + @Inject + private CheckGeneratorExtensions checkGeneratorExtensions; - @Inject TypesFactory typesFactory - @Inject CheckLocationInFileProvider locationInFileProvider + @Inject + private CheckGeneratorNaming checkGeneratorNaming; - @Inject extension CheckGeneratorExtensions - @Inject extension CheckGeneratorNaming - @Inject extension JvmTypesBuilder + @Inject + private JvmTypesBuilder jvmTypesBuilder; - def dispatch infer(CheckCatalog catalog, IJvmDeclaredTypeAcceptor acceptor, boolean preIndexingPhase) { + // CHECKSTYLE:CONSTANTS-OFF the repeated literals are Java source fragments emitted by this generator, not nameable constants + // CHECKSTYLE:CHECK-OFF LambdaBodyLength the model-inference closures mirror the Xtext JvmTypesBuilder API and are kept whole + protected void _infer(final CheckCatalog catalog, final IJvmDeclaredTypeAcceptor acceptor, final boolean preIndexingPhase) { // The xbase automatic scoping mechanism (typeRef()) cannot find secondary classes in the same resource. It can // only find indexed resources (either in the JDT index or in the xtext index). However, we'll initialize the // JVM validator class before the resource gets indexed, so the JVM catalog class cannot be found yet when we // create the injection in the validator. Therefore, remember the class here directly, and set it directly // in the validator, completely bypassing any scoping. - if (preIndexingPhase) return; - val catalogClass = catalog.toClass(catalog.qualifiedCatalogClassName); - val issueCodeToLabelMapTypeRef = typeRef(ImmutableMap, typeRef(String), typeRef(String)) - acceptor.accept(catalogClass, [ - val parentType = checkedTypeRef(catalog, typeof(AbstractIssue)); - if (parentType !== null) { - superTypes += parentType; + if (preIndexingPhase) { + return; + } + final JvmGenericType catalogClass = jvmTypesBuilder.toClass(catalog, checkGeneratorNaming.qualifiedCatalogClassName(catalog)); + final JvmTypeReference issueCodeToLabelMapTypeRef = _typeReferenceBuilder.typeRef(ImmutableMap.class, _typeReferenceBuilder.typeRef(String.class), _typeReferenceBuilder.typeRef(String.class)); + acceptor.accept(catalogClass, (final JvmGenericType it) -> { + final JvmTypeReference parentType = checkedTypeRef(catalog, AbstractIssue.class); + if (parentType != null) { + it.getSuperTypes().add(parentType); } - annotations += createAnnotation(checkedTypeRef(catalog, typeof(Singleton)), []) - documentation = '''Issues for «catalog.name».'''; - members += createInjectedField(catalog, 'checkConfigurationStoreService', checkedTypeRef(catalog, typeof(ICheckConfigurationStoreService))); + Iterables.addAll(it.getAnnotations(), createAnnotation(checkedTypeRef(catalog, Singleton.class), (final JvmAnnotationReference it1) -> { + })); + jvmTypesBuilder.setDocumentation(it, "Issues for " + catalog.getName() + "."); + Iterables.addAll(it.getMembers(), createInjectedField(catalog, "checkConfigurationStoreService", checkedTypeRef(catalog, ICheckConfigurationStoreService.class))); // Create map of issue code to label and associated getter - members += catalog.toField(issueCodeToLabelMapFieldName, issueCodeToLabelMapTypeRef, [ - static = true - final = true + it.getMembers().add(jvmTypesBuilder.toField(catalog, checkGeneratorNaming.issueCodeToLabelMapFieldName(), issueCodeToLabelMapTypeRef, (final JvmField it1) -> { + it1.setStatic(true); + it1.setFinal(true); // Get all issue codes and labels - val issues = catalog.checkAndImplementationIssues + final Iterable issues = checkGeneratorExtensions.checkAndImplementationIssues(catalog); // Use a TreeMap to eliminate duplicates, // and also to sort by qualified issue code name so autogenerated files are more readable and less prone to spurious ordering changes. // Do this when compiling the Check, to avoid discovering duplicates at runtime. - val sortedUniqueQualifiedIssueCodeNamesAndLabels = new TreeMap(); - for (issue : issues) { - val qualifiedIssueCodeName = issue.qualifiedIssueCodeName(); - val issueLabel = issue.issueLabel().escapeJava; - val existingIssueLabel = sortedUniqueQualifiedIssueCodeNamesAndLabels.putIfAbsent(qualifiedIssueCodeName, issueLabel); - if (null !== existingIssueLabel && issueLabel != existingIssueLabel) { + final SortedMap sortedUniqueQualifiedIssueCodeNamesAndLabels = new TreeMap(); + for (final XIssueExpression issue : issues) { + final String qualifiedIssueCodeName = checkGeneratorExtensions.qualifiedIssueCodeName(issue); + final String issueLabel = StringEscapeUtils.escapeJava(checkGeneratorExtensions.issueLabel(issue)); + final String existingIssueLabel = sortedUniqueQualifiedIssueCodeNamesAndLabels.putIfAbsent(qualifiedIssueCodeName, issueLabel); + if (null != existingIssueLabel && !Objects.equals(issueLabel, existingIssueLabel)) { // This qualified issue code name is already in the map, with a different label. Fail the build. - throw new IllegalArgumentException('''Multiple issues found with qualified issue code name: «qualifiedIssueCodeName»''') + throw new IllegalArgumentException("Multiple issues found with qualified issue code name: " + qualifiedIssueCodeName); } } - initializer = [append(''' - «ImmutableMap.simpleName».<«String.simpleName», «String.simpleName»>builderWithExpectedSize(«sortedUniqueQualifiedIssueCodeNamesAndLabels.entrySet.size») - «FOR qualifiedIssueCodeNameAndLabel : sortedUniqueQualifiedIssueCodeNamesAndLabels.entrySet» - .put(«qualifiedIssueCodeNameAndLabel.key», "«qualifiedIssueCodeNameAndLabel.value»") - «ENDFOR» - .build() - ''')] - ]) - members += catalog.toMethod(issueCodeToLabelMapFieldName.fieldGetterName, issueCodeToLabelMapTypeRef, [ - documentation = ''' - Get map of issue code to label for «catalog.name». - - @returns Map of issue code to label for «catalog.name». - '''; - static = true - final = true - body = '''return «issueCodeToLabelMapFieldName»;''' - ]) - - members += catalog.allChecks.map(c|createIssue(catalog, c)).flatten.filterNull; - ]); - - acceptor.accept(catalog.toClass(catalog.qualifiedValidatorClassName), [ - val parentType = checkedTypeRef(catalog, typeof(DispatchingCheckImpl)); - if (parentType !== null) { - superTypes += parentType; + jvmTypesBuilder.setInitializer(it1, (final ITreeAppendable appendable) -> { + final StringConcatenation builder = new StringConcatenation(); + builder.append(ImmutableMap.class.getSimpleName()); + builder.append(".<"); + builder.append(String.class.getSimpleName()); + builder.append(", "); + builder.append(String.class.getSimpleName()); + builder.append(">builderWithExpectedSize("); + builder.append(sortedUniqueQualifiedIssueCodeNamesAndLabels.entrySet().size()); + builder.append(")"); + builder.newLineIfNotEmpty(); + for (final Map.Entry qualifiedIssueCodeNameAndLabel : sortedUniqueQualifiedIssueCodeNamesAndLabels.entrySet()) { + builder.append(" "); + builder.append(".put("); + builder.append(qualifiedIssueCodeNameAndLabel.getKey(), " "); + builder.append(", \""); + builder.append(qualifiedIssueCodeNameAndLabel.getValue(), " "); + builder.append("\")"); + builder.newLineIfNotEmpty(); + } + builder.append(" "); + builder.append(".build()"); + builder.newLine(); + appendable.append(builder); + }); + })); + it.getMembers().add(jvmTypesBuilder.toMethod(catalog, checkGeneratorNaming.fieldGetterName(checkGeneratorNaming.issueCodeToLabelMapFieldName()), issueCodeToLabelMapTypeRef, (final JvmOperation it1) -> { + final StringConcatenation builder = new StringConcatenation(); + builder.append("Get map of issue code to label for "); + builder.append(catalog.getName()); + builder.append("."); + builder.newLineIfNotEmpty(); + builder.newLine(); + builder.append("@returns Map of issue code to label for "); + builder.append(catalog.getName()); + builder.append("."); + builder.newLineIfNotEmpty(); + jvmTypesBuilder.setDocumentation(it1, builder.toString()); + it1.setStatic(true); + it1.setFinal(true); + jvmTypesBuilder.setBody(it1, (final ITreeAppendable appendable) -> appendable.append("return " + checkGeneratorNaming.issueCodeToLabelMapFieldName() + ";")); + })); + + Iterables.addAll(it.getMembers(), IterableExtensions.filterNull(Iterables.concat(ListExtensions.>map(catalog.getAllChecks(), (final Check c) -> createIssue(catalog, c))))); + }); + + acceptor.accept(jvmTypesBuilder.toClass(catalog, checkGeneratorNaming.qualifiedValidatorClassName(catalog)), (final JvmGenericType it) -> { + final JvmTypeReference parentType = checkedTypeRef(catalog, DispatchingCheckImpl.class); + if (parentType != null) { + it.getSuperTypes().add(parentType); } // Constructor will be added automatically. - documentation = ''' - Validator for «catalog.name».'''; + jvmTypesBuilder.setDocumentation(it, "Validator for " + catalog.getName() + "."); // Create catalog injections - members += createInjectedField(catalog, catalog.catalogInstanceName, typeRef(catalogClass)); + Iterables.addAll(it.getMembers(), createInjectedField(catalog, checkGeneratorNaming.catalogInstanceName(catalog), _typeReferenceBuilder.typeRef(catalogClass))); // Create fields - members += catalog.members.map(m|m.toField(m.name, m.type) [initializer = m.value; it.addAnnotations(m.annotations);]); + Iterables.addAll(it.getMembers(), IterableExtensions.filterNull(ListExtensions.map(catalog.getMembers(), (final Member m) -> jvmTypesBuilder.toField(m, m.getName(), m.getType(), (final JvmField it1) -> { + jvmTypesBuilder.setInitializer(it1, m.getValue()); + jvmTypesBuilder.addAnnotations(it1, m.getAnnotations()); + })))); // Create catalog name function - members += catalog.toMethod('getQualifiedCatalogName', typeRef(typeof(String))) [ - body = [append('''return "«catalog.packageName».«catalog.name»";''')]; - ]; + it.getMembers().add(jvmTypesBuilder.toMethod(catalog, "getQualifiedCatalogName", _typeReferenceBuilder.typeRef(String.class), (final JvmOperation it1) -> { + jvmTypesBuilder.setBody(it1, (final ITreeAppendable appendable) -> appendable.append("return \"" + catalog.getPackageName() + "." + catalog.getName() + "\";")); + })); // Create getter for map of issue code to label - members += catalog.toMethod(issueCodeToLabelMapFieldName.fieldGetterName, issueCodeToLabelMapTypeRef, [ - final = true - body = '''return «catalog.catalogClassName».«issueCodeToLabelMapFieldName.fieldGetterName»();''' - ]) + it.getMembers().add(jvmTypesBuilder.toMethod(catalog, checkGeneratorNaming.fieldGetterName(checkGeneratorNaming.issueCodeToLabelMapFieldName()), issueCodeToLabelMapTypeRef, (final JvmOperation it1) -> { + it1.setFinal(true); + jvmTypesBuilder.setBody(it1, (final ITreeAppendable appendable) -> appendable.append("return " + checkGeneratorNaming.catalogClassName(catalog) + "." + checkGeneratorNaming.fieldGetterName(checkGeneratorNaming.issueCodeToLabelMapFieldName()) + "();")); + })); - members += createDispatcherMethod(catalog); + it.getMembers().add(createDispatcherMethod(catalog)); // Create methods for contexts in checks - val allChecks = catalog.checks + catalog.categories.map(cat|cat.checks).flatten; - members += allChecks.map(chk|createCheck(chk)).flatten; + final List checks = catalog.getChecks(); + final Iterable flattenedCategoryChecks = Iterables.concat(ListExtensions.>map(catalog.getCategories(), (final Category cat) -> cat.getChecks())); + final Iterable allChecks = Iterables.concat(checks, flattenedCategoryChecks); + Iterables.addAll(it.getMembers(), IterableExtensions.filterNull(Iterables.concat(IterableExtensions.>map(allChecks, (final Check chk) -> createCheck(chk))))); // Create methods for stand-alone context implementations - members += catalog.implementations.map(impl|createCheckMethod(impl.context)).filterNull; - ]); - acceptor.accept(catalog.toClass(catalog.qualifiedPreferenceInitializerClassName), [ - val parentType = checkedTypeRef(catalog, typeof(AbstractPreferenceInitializer)); - if (parentType !== null) { - superTypes += parentType; + Iterables.addAll(it.getMembers(), IterableExtensions.filterNull(ListExtensions.map(catalog.getImplementations(), (final Implementation impl) -> createCheckMethod(impl.getContext())))); + }); + acceptor.accept(jvmTypesBuilder.toClass(catalog, checkGeneratorNaming.qualifiedPreferenceInitializerClassName(catalog)), (final JvmGenericType it) -> { + final JvmTypeReference parentType = checkedTypeRef(catalog, AbstractPreferenceInitializer.class); + if (parentType != null) { + it.getSuperTypes().add(parentType); } - members += catalog.toField('RUNTIME_NODE_NAME', typeRef(typeof(String))) [ - static = true; - final = true; - initializer = [append('"' + catalog.bundleName + '"')]; - ]; - members += createFormalParameterFields(catalog); - members += createPreferenceInitializerMethods(catalog); - ]); + it.getMembers().add(jvmTypesBuilder.toField(catalog, "RUNTIME_NODE_NAME", _typeReferenceBuilder.typeRef(String.class), (final JvmField it1) -> { + it1.setStatic(true); + it1.setFinal(true); + jvmTypesBuilder.setInitializer(it1, (final ITreeAppendable appendable) -> appendable.append("\"" + checkGeneratorExtensions.bundleName(catalog) + "\"")); + })); + Iterables.addAll(it.getMembers(), createFormalParameterFields(catalog)); + Iterables.addAll(it.getMembers(), createPreferenceInitializerMethods(catalog)); + }); } - - private def JvmOperation createDispatcherMethod(CheckCatalog catalog) { - val objectBaseJavaTypeRef = checkedTypeRef(catalog, EObject); - return catalog.toMethod("validate", typeRef("void"), [ - visibility = JvmVisibility::PUBLIC; - parameters += catalog.toParameter("checkMode", checkedTypeRef(catalog, CheckMode)); - parameters += catalog.toParameter("object", objectBaseJavaTypeRef); - parameters += catalog.toParameter("diagnosticCollector", checkedTypeRef(catalog, DiagnosticCollector)); - annotations += createAnnotation(checkedTypeRef(catalog, typeof(Override)), []); - body = [out | emitDispatcherMethodBody(out, catalog, objectBaseJavaTypeRef)]; - ]); + // CHECKSTYLE:CHECK-ON LambdaBodyLength + + private JvmOperation createDispatcherMethod(final CheckCatalog catalog) { + final JvmTypeReference objectBaseJavaTypeRef = checkedTypeRef(catalog, EObject.class); + return jvmTypesBuilder.toMethod(catalog, "validate", _typeReferenceBuilder.typeRef("void"), (final JvmOperation it) -> { + it.setVisibility(JvmVisibility.PUBLIC); + it.getParameters().add(jvmTypesBuilder.toParameter(catalog, "checkMode", checkedTypeRef(catalog, CheckMode.class))); + it.getParameters().add(jvmTypesBuilder.toParameter(catalog, "object", objectBaseJavaTypeRef)); + it.getParameters().add(jvmTypesBuilder.toParameter(catalog, "diagnosticCollector", checkedTypeRef(catalog, DiagnosticCollector.class))); + Iterables.addAll(it.getAnnotations(), createAnnotation(checkedTypeRef(catalog, Override.class), (final JvmAnnotationReference it1) -> { + })); + jvmTypesBuilder.setBody(it, (final ITreeAppendable out) -> emitDispatcherMethodBody(out, catalog, objectBaseJavaTypeRef)); + }); } - private def void emitDispatcherMethodBody(ITreeAppendable out, CheckCatalog catalog, JvmTypeReference objectBaseJavaTypeRef) { - /* A catalog may contain both Check and Implementation objects, + private void emitDispatcherMethodBody(final ITreeAppendable out, final CheckCatalog catalog, final JvmTypeReference objectBaseJavaTypeRef) { + /* + * A catalog may contain both Check and Implementation objects, * which in turn may contain Context objects. * Categories may optionally be used for grouping checks, and * we can include categorized checks by using getAllChecks(). * We only consider Context objects with a typed contextVariable. */ - val allContexts = (catalog.allChecks.map(chk | chk.contexts).flatten + - catalog.implementations.map(impl | impl.context).filterNull) - .filter(ctx | ctx.contextVariable?.type !== null); + final Iterable checkContexts = Iterables.concat(ListExtensions.>map(catalog.getAllChecks(), (final Check chk) -> chk.getContexts())); + final Iterable implementationContexts = IterableExtensions.filterNull(ListExtensions.map(catalog.getImplementations(), (final Implementation impl) -> impl.getContext())); + final Iterable allContexts = IterableExtensions.filter(Iterables.concat(checkContexts, implementationContexts), (final Context ctx) -> { + JvmTypeReference type = null; + if (ctx.getContextVariable() != null) { + type = ctx.getContextVariable().getType(); + } + return type != null; + }); - /* Contexts grouped by CheckType. + /* + * Contexts grouped by CheckType. * We use an OrderedMap for deterministic ordering of check type checks. * For Context objects we retain their order of appearance, apart from groupings. */ - val contextsByCheckType = new TreeMap>(); - for (Context context : allContexts) { - contextsByCheckType.compute(checkType(context), [k, lst | lst ?: new ArrayList()]).add(context); + final SortedMap> contextsByCheckType = new TreeMap>(); + for (final Context context : allContexts) { + contextsByCheckType.compute(checkGeneratorExtensions.checkType(context), (final CheckType k, final List lst) -> lst != null ? lst : new ArrayList()).add(context); } - val baseTypeName = objectBaseJavaTypeRef.qualifiedName; + final String baseTypeName = objectBaseJavaTypeRef.getQualifiedName(); - for (val iterator = contextsByCheckType.entrySet.iterator(); iterator.hasNext(); ) { - val entry = iterator.next(); - val checkType = '''CheckType.«entry.key»'''; + for (final Iterator>> iterator = contextsByCheckType.entrySet().iterator(); iterator.hasNext();) { + final Map.Entry> entry = iterator.next(); + final String checkType = "CheckType." + entry.getKey(); - out.append('''if (checkMode.shouldCheck(«checkType»)) {'''); - out.increaseIndentation; - out.newLine; - out.append('''diagnosticCollector.setCurrentCheckType(«checkType»);'''); - emitInstanceOfConditionals(out, entry.value, catalog, baseTypeName); // with preceding newline for each - out.decreaseIndentation; - out.newLine; + out.append("if (checkMode.shouldCheck(" + checkType + ")) {"); + out.increaseIndentation(); + out.newLine(); + out.append("diagnosticCollector.setCurrentCheckType(" + checkType + ");"); + emitInstanceOfConditionals(out, entry.getValue(), catalog, baseTypeName); // with preceding newline for each + out.decreaseIndentation(); + out.newLine(); out.append("}"); - if (iterator.hasNext()) // not at method body end - out.newLine; // separator between mode checks + if (iterator.hasNext()) { // not at method body end + out.newLine(); // separator between mode checks + } } } - private def void emitInstanceOfConditionals(ITreeAppendable out, List contexts, CheckCatalog catalog, String baseTypeName) { - /* Contexts grouped by fully qualified variable type name, + private void emitInstanceOfConditionals(final ITreeAppendable out, final List contexts, final CheckCatalog catalog, final String baseTypeName) { + /* + * Contexts grouped by fully qualified variable type name, * otherwise in order of appearance. */ - val contextsByVarType = new TreeMap>(); - for (Context context : contexts) { - contextsByVarType.compute(context.contextVariable.type.qualifiedName, - [k, lst | lst ?: new ArrayList()] - ).add(context); + final SortedMap> contextsByVarType = new TreeMap>(); + for (final Context context : contexts) { + contextsByVarType.compute(context.getContextVariable().getType().getQualifiedName(), (final String k, final List lst) -> lst != null ? lst : new ArrayList()).add(context); } /* Ordering for context variable type checks. */ - val List contextVarTypes = contexts.map([x | x.contextVariable.type]); - val forest = InstanceOfCheckOrderer.orderTypes(contextVarTypes); + final List contextVarTypes = ListExtensions.map(contexts, (final Context x) -> x.getContextVariable().getType()); + final InstanceOfCheckOrderer.Forest forest = InstanceOfCheckOrderer.orderTypes(contextVarTypes); emitInstanceOfTree(out, forest, null, contextsByVarType, catalog, baseTypeName, 0); } - private def void emitInstanceOfTree(ITreeAppendable out, InstanceOfCheckOrderer.Forest forest, String node, Map> contextsByVarType, CheckCatalog catalog, String baseTypeName, int level) { - if (node !== null) { - var String typeName = node; - if (typeName == baseTypeName) + private void emitInstanceOfTree(final ITreeAppendable out, final InstanceOfCheckOrderer.Forest forest, final String node, final Map> contextsByVarType, final CheckCatalog catalog, final String baseTypeName, final int level) { + if (node != null) { + String typeName = node; + if (Objects.equals(typeName, baseTypeName)) { typeName = null; - val varName = if (typeName === null) "object" else "castObject" + (if (level > 1) Integer.toString(level) else ""); + } + final String varName; + if (typeName == null) { + varName = "object"; + } else { + varName = "castObject" + (level > 1 ? Integer.toString(level) : ""); + } - out.newLine; - out.append('''«IF typeName !== null»if (object instanceof final «typeName» «varName») «ENDIF»{'''); - out.increaseIndentation; + out.newLine(); + String prefix = ""; + if (typeName != null) { + prefix = "if (object instanceof final " + typeName + " " + varName + ") "; + } + out.append(prefix + "{"); + out.increaseIndentation(); - val contexts = contextsByVarType.get(node); - for (context : contexts) { + final List contexts = contextsByVarType.get(node); + for (final Context context : contexts) { emitCheckMethodCall(out, varName, context, catalog); // with preceding newline } } - for (child : forest.getSubTypes(node)) { + final Collection subTypes = forest.getSubTypes(node); + for (final String child : subTypes) { emitInstanceOfTree(out, forest, child, contextsByVarType, catalog, baseTypeName, level + 1); } - if (node !== null) { - out.decreaseIndentation; - out.newLine; - out.append('}'); + if (node != null) { + out.decreaseIndentation(); + out.newLine(); + out.append("}"); } } - private def void emitCheckMethodCall(ITreeAppendable out, String varName, Context context, CheckCatalog catalog) { - val methodName = generateContextMethodName(context); - val jMethodName = toJavaLiteral(methodName); - val qMethodName = toJavaLiteral(catalog.name, methodName); - - out.newLine; - out.append(''' - validate(«jMethodName», «qMethodName», object, - () -> «methodName»(«varName», diagnosticCollector), diagnosticCollector);'''); + private void emitCheckMethodCall(final ITreeAppendable out, final String varName, final Context context, final CheckCatalog catalog) { + final String methodName = generateContextMethodName(context); + final String jMethodName = toJavaLiteral(methodName); + final String qMethodName = toJavaLiteral(catalog.getName(), methodName); + + out.newLine(); + final StringConcatenation builder = new StringConcatenation(); + builder.append("validate("); + builder.append(jMethodName); + builder.append(", "); + builder.append(qMethodName); + builder.append(", object,"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("() -> "); + builder.append(methodName, " "); + builder.append("("); + builder.append(varName, " "); + builder.append(", diagnosticCollector), diagnosticCollector);"); + out.append(builder); } - private def String toJavaLiteral(String... strings) { - return '''"«Strings::convertToJavaString(String.join(".", strings))»"'''; + private String toJavaLiteral(final String... strings) { + return "\"" + Strings.convertToJavaString(String.join(".", strings)) + "\""; } - private def Iterable createInjectedField(CheckCatalog context, String fieldName, JvmTypeReference type) { + private Iterable createInjectedField(final CheckCatalog context, final String fieldName, final JvmTypeReference type) { // Generate @Inject private typeName fieldName; - if (type === null) { - return Collections::emptyList; + if (type == null) { + return Collections.emptyList(); } - val field = typesFactory.createJvmField(); - field.simpleName = fieldName; - field.visibility = JvmVisibility::PRIVATE; - field.type = cloneWithProxies(type); - field.annotations += createAnnotation(checkedTypeRef(context, typeof(Inject)), []); - return Collections::singleton(field); + final JvmField field = typesFactory.createJvmField(); + field.setSimpleName(fieldName); + field.setVisibility(JvmVisibility.PRIVATE); + field.setType(jvmTypesBuilder.cloneWithProxies(type)); + Iterables.addAll(field.getAnnotations(), createAnnotation(checkedTypeRef(context, Inject.class), (final JvmAnnotationReference it) -> { + })); + return Collections.singleton(field); } - private def Iterable createCheck(Check chk) { + private Iterable createCheck(final Check chk) { // If we don't have FormalParameters, there's no need to do all this song and dance with inner classes. - if (chk.formalParameters.empty) { - return chk.contexts.map(ctx|createCheckMethod(ctx) as JvmMember); + if (chk.getFormalParameters().isEmpty()) { + return ListExtensions.map(chk.getContexts(), (final Context ctx) -> createCheckMethod(ctx)); } else { return createCheckWithParameters(chk); } } - private def Iterable createCheckWithParameters(Check chk) { + private Iterable createCheckWithParameters(final Check chk) { // Generate an inner class, plus a field holding an instance of that class. // Put the formal parameters into that class as fields. // For each check context, generate a run method. @@ -330,317 +419,439 @@ private def Iterable createCheckWithParameters(Check chk) { // This is the only way I found to make those formal parameters visible in the check constraints... // The generated Java looks a bit strange, because we suppress actually generating these fields, as we // don't use them; we only need them for scoping based on this inferred model. - val List newMembers = Lists::newArrayList; + final List newMembers = Lists.newArrayList(); // First the class - val checkClass = chk.toClass(chk.name.toFirstUpper + 'Class') [ - superTypes += typeRef(typeof(Object)); - visibility = JvmVisibility::PRIVATE; + final JvmGenericType checkClass = jvmTypesBuilder.toClass(chk, StringExtensions.toFirstUpper(chk.getName()) + "Class", (final JvmGenericType it) -> { + it.getSuperTypes().add(_typeReferenceBuilder.typeRef(Object.class)); + it.setVisibility(JvmVisibility.PRIVATE); // Add a fields for the parameters, so that they can be linked. We suppress generation of these fields in the generator, // and replace all references by calls to the getter function in the catalog. - members += chk.formalParameters.filter(f|f.type !== null && f.name !== null).map(f|f.toField(f.name, f.type) [final = true]); - ]; - newMembers += checkClass; - newMembers += chk.toField(chk.name.toFirstLower + 'Impl', typeRef(checkClass)) [initializer = [append('''new «checkClass.simpleName»()''')]]; - newMembers += chk.contexts.map(ctx|createCheckCaller(ctx, chk)).filterNull; + Iterables.addAll(it.getMembers(), IterableExtensions.map(IterableExtensions.filter(chk.getFormalParameters(), (final FormalParameter f) -> f.getType() != null && f.getName() != null), (final FormalParameter f) -> jvmTypesBuilder.toField(f, f.getName(), f.getType(), (final JvmField it1) -> it1.setFinal(true)))); + }); + newMembers.add(checkClass); + newMembers.add(jvmTypesBuilder.toField(chk, StringExtensions.toFirstLower(chk.getName()) + "Impl", _typeReferenceBuilder.typeRef(checkClass), (final JvmField it) -> { + jvmTypesBuilder.setInitializer(it, (final ITreeAppendable appendable) -> appendable.append("new " + checkClass.getSimpleName() + "()")); + })); + Iterables.addAll(newMembers, IterableExtensions.filterNull(ListExtensions.map(chk.getContexts(), (final Context ctx) -> createCheckCaller(ctx, chk)))); // If we create these above in the class initializer, the types of the context variables somehow are not resolved yet. - checkClass.members += chk.contexts.map(ctx|createCheckExecution(ctx)).filterNull; + Iterables.addAll(checkClass.getMembers(), IterableExtensions.filterNull(ListExtensions.map(chk.getContexts(), (final Context ctx) -> createCheckExecution(ctx)))); return newMembers; } - private def JvmOperation createCheckExecution(Context ctx) { - if (ctx === null || ctx.contextVariable === null) { + private JvmOperation createCheckExecution(final Context ctx) { + if (ctx == null || ctx.getContextVariable() == null) { return null; } - val String functionName = 'run' + ctx.contextVariable.type?.simpleName.toFirstUpper; - ctx.toMethod(functionName, typeRef('void')) [ - parameters += ctx.toParameter(if (ctx.contextVariable.name === null) CheckConstants::IT else ctx.contextVariable.name, ctx.contextVariable.type); - parameters += ctx.toParameter("diagnosticCollector", checkedTypeRef(ctx, DiagnosticCollector)); - body = ctx.constraint; - ] + final JvmTypeReference contextVariableType = ctx.getContextVariable().getType(); + String simpleName = null; + if (contextVariableType != null) { + simpleName = contextVariableType.getSimpleName(); + } + final String functionName = "run" + StringExtensions.toFirstUpper(simpleName); + return jvmTypesBuilder.toMethod(ctx, functionName, _typeReferenceBuilder.typeRef("void"), (final JvmOperation it) -> { + final String parameterName = ctx.getContextVariable().getName() == null ? CheckConstants.IT : ctx.getContextVariable().getName(); + it.getParameters().add(jvmTypesBuilder.toParameter(ctx, parameterName, ctx.getContextVariable().getType())); + it.getParameters().add(jvmTypesBuilder.toParameter(ctx, "diagnosticCollector", checkedTypeRef(ctx, DiagnosticCollector.class))); + jvmTypesBuilder.setBody(it, ctx.getConstraint()); + }); } - private def Iterable createCheckAnnotation (Context ctx) { - val checkTypeTypeRef = checkedTypeRef(ctx, typeof(CheckType)); - if (checkTypeTypeRef === null) { - return Collections::emptyList; + private Iterable createCheckAnnotation(final Context ctx) { + final JvmTypeReference checkTypeTypeRef = checkedTypeRef(ctx, CheckType.class); + if (checkTypeTypeRef == null) { + return Collections.emptyList(); } - val XFeatureCall featureCall = XbaseFactory::eINSTANCE.createXFeatureCall(); - featureCall.feature = checkTypeTypeRef.type; - featureCall.typeLiteral = true; - val XMemberFeatureCall memberCall = XbaseFactory::eINSTANCE.createXMemberFeatureCall(); - memberCall.memberCallTarget = featureCall; + final XFeatureCall featureCall = XbaseFactory.eINSTANCE.createXFeatureCall(); + featureCall.setFeature(checkTypeTypeRef.getType()); + featureCall.setTypeLiteral(true); + final XMemberFeatureCall memberCall = XbaseFactory.eINSTANCE.createXMemberFeatureCall(); + memberCall.setMemberCallTarget(featureCall); // The grammar doesn't use the CheckType constants directly... - var String name = checkTypeQName(ctx); - val int i = name.lastIndexOf('.'); + String name = checkGeneratorExtensions.checkTypeQName(ctx); + final int i = name.lastIndexOf('.'); if (i >= 0) { - name = name.substring(i+1); + name = name.substring(i + 1); } - memberCall.feature = (checkTypeTypeRef.type as JvmDeclaredType).findAllFeaturesByName(name).head; + memberCall.setFeature(IterableExtensions.head(((JvmDeclaredType) checkTypeTypeRef.getType()).findAllFeaturesByName(name))); // memberCall needs to belong to a resource. // We add it as a separate model to the context's resource. - ctx.eResource.contents.add(memberCall) + ctx.eResource().getContents().add(memberCall); - return createAnnotation(checkedTypeRef(ctx, typeof(org.eclipse.xtext.validation.Check)), [ - explicitValues += memberCall.toJvmAnnotationValue(); - ]); + return createAnnotation(checkedTypeRef(ctx, org.eclipse.xtext.validation.Check.class), (final JvmAnnotationReference it) -> { + it.getExplicitValues().add(jvmTypesBuilder.toJvmAnnotationValue(memberCall)); + }); } - private def JvmOperation createCheckCaller(Context ctx, Check chk) { - if (ctx === null || ctx.contextVariable === null) { + private JvmOperation createCheckCaller(final Context ctx, final Check chk) { + if (ctx == null || ctx.getContextVariable() == null) { return null; } - val String functionName = chk.name.toFirstLower + ctx.contextVariable.type?.simpleName; + final JvmTypeReference contextVariableType = ctx.getContextVariable().getType(); + String simpleName = null; + if (contextVariableType != null) { + simpleName = contextVariableType.getSimpleName(); + } + final String functionName = StringExtensions.toFirstLower(chk.getName()) + simpleName; // To make the formal parameter visible, we have to generate quite a bit... I see no way to get the XVariableDeclaration for them // into the XBlockExpression of ctx.constraint. Just copying them doesn't work; modifies the source model! // Therefore, we generate something new: each check becomes a local class - ctx.toMethod(functionName, typeRef('void')) [ - parameters += ctx.toParameter("context", ctx.contextVariable.type); - parameters += ctx.toParameter("diagnosticCollector", checkedTypeRef(ctx, DiagnosticCollector)); - annotations += createCheckAnnotation(ctx); - documentation = functionName + '.'; // Well, that's not very helpful, but it is what the old compiler did... - body = [append(''' - «chk.name.toFirstLower + 'Impl'».run«ctx.contextVariable.type?.simpleName.toFirstUpper»(context, diagnosticCollector);''' - )] - ] + return jvmTypesBuilder.toMethod(ctx, functionName, _typeReferenceBuilder.typeRef("void"), (final JvmOperation it) -> { + it.getParameters().add(jvmTypesBuilder.toParameter(ctx, "context", ctx.getContextVariable().getType())); + it.getParameters().add(jvmTypesBuilder.toParameter(ctx, "diagnosticCollector", checkedTypeRef(ctx, DiagnosticCollector.class))); + Iterables.addAll(it.getAnnotations(), createCheckAnnotation(ctx)); + jvmTypesBuilder.setDocumentation(it, functionName + "."); // Well, that's not very helpful, but it is what the old compiler did... + jvmTypesBuilder.setBody(it, (final ITreeAppendable appendable) -> { + final JvmTypeReference innerContextVariableType = ctx.getContextVariable().getType(); + String innerSimpleName = null; + if (innerContextVariableType != null) { + innerSimpleName = innerContextVariableType.getSimpleName(); + } + appendable.append(StringExtensions.toFirstLower(chk.getName()) + "Impl" + ".run" + StringExtensions.toFirstUpper(innerSimpleName) + "(context, diagnosticCollector);"); + }); + }); } - private def JvmOperation createCheckMethod(Context ctx) { + private JvmOperation createCheckMethod(final Context ctx) { // Simple case for contexts of checks that do not have formal parameters. No need to generate nested classes for these. - if (ctx === null || ctx.contextVariable === null) { + if (ctx == null || ctx.getContextVariable() == null) { return null; } - val String functionName = generateContextMethodName(ctx); - - ctx.toMethod(functionName, typeRef('void')) [ - parameters += ctx.toParameter(if (ctx.contextVariable.name === null) CheckConstants::IT else ctx.contextVariable.name, ctx.contextVariable.type); - parameters += ctx.toParameter("diagnosticCollector", checkedTypeRef(ctx, DiagnosticCollector)); - annotations += createCheckAnnotation(ctx); - documentation = functionName + '.'; // Well, that's not very helpful, but it is what the old compiler did... - body = ctx.constraint; - ] + final String functionName = generateContextMethodName(ctx); + + return jvmTypesBuilder.toMethod(ctx, functionName, _typeReferenceBuilder.typeRef("void"), (final JvmOperation it) -> { + final String parameterName = ctx.getContextVariable().getName() == null ? CheckConstants.IT : ctx.getContextVariable().getName(); + it.getParameters().add(jvmTypesBuilder.toParameter(ctx, parameterName, ctx.getContextVariable().getType())); + it.getParameters().add(jvmTypesBuilder.toParameter(ctx, "diagnosticCollector", checkedTypeRef(ctx, DiagnosticCollector.class))); + Iterables.addAll(it.getAnnotations(), createCheckAnnotation(ctx)); + jvmTypesBuilder.setDocumentation(it, functionName + "."); // Well, that's not very helpful, but it is what the old compiler did... + jvmTypesBuilder.setBody(it, ctx.getConstraint()); + }); } - private def String generateContextMethodName(Context ctx) { - return switch container : ctx.eContainer { - Check : container.name - Implementation: container.name - }.toFirstLower + ctx.contextVariable.type?.simpleName; + private String generateContextMethodName(final Context ctx) { + final EObject container = ctx.eContainer(); + final String baseName; + if (container instanceof Check check) { + baseName = check.getName(); + } else if (container instanceof Implementation implementation) { + baseName = implementation.getName(); + } else { + baseName = null; + } + final JvmTypeReference contextVariableType = ctx.getContextVariable().getType(); + String simpleName = null; + if (contextVariableType != null) { + simpleName = contextVariableType.getSimpleName(); + } + return StringExtensions.toFirstLower(baseName) + simpleName; } // CheckCatalog - private def Iterable createIssue(CheckCatalog catalog, Check check) { - val List members = Lists::newArrayList(); - for (FormalParameter parameter : check.formalParameters) { - val JvmTypeReference returnType = parameter.type; - if (returnType !== null && !returnType.eIsProxy) { - val String returnName = returnType.qualifiedName; - val String operation = switch returnName { - case 'java.lang.Boolean' : 'getBoolean' - case 'boolean' : 'getBoolean' - case 'java.lang.Integer' : 'getInt' - case 'int' : 'getInt' - case 'java.util.List' : 'getStrings' - case 'java.util.List' : 'getBooleans' - case 'java.util.List' : 'getIntegers' - default : 'getString' + // CHECKSTYLE:CHECK-OFF LambdaBodyLength the model-inference closures mirror the Xtext JvmTypesBuilder API and are kept whole + private Iterable createIssue(final CheckCatalog catalog, final Check check) { + final List members = Lists.newArrayList(); + for (final FormalParameter parameter : check.getFormalParameters()) { + final JvmTypeReference returnType = parameter.getType(); + if (returnType != null && !returnType.eIsProxy()) { + final String returnName = returnType.getQualifiedName(); + final String operation; + if (returnName != null) { + operation = switch (returnName) { + case "java.lang.Boolean" -> "getBoolean"; + case "boolean" -> "getBoolean"; + case "java.lang.Integer" -> "getInt"; + case "int" -> "getInt"; + case "java.util.List" -> "getStrings"; + case "java.util.List" -> "getBooleans"; + case "java.util.List" -> "getIntegers"; + default -> "getString"; + }; + } else { + operation = "getString"; } - val String parameterKey = CheckPropertiesGenerator::parameterKey(parameter, check); - var String defaultName = 'null'; - if (parameter.right !== null) { - defaultName = parameter.formalParameterGetterName.splitCamelCase.toUpperCase + '_DEFAULT'; + final String parameterKey = CheckPropertiesGenerator.parameterKey(parameter, check); + String defaultName = "null"; + if (parameter.getRight() != null) { + defaultName = CheckGeneratorExtensions.splitCamelCase(checkGeneratorNaming.formalParameterGetterName(parameter)).toUpperCase() + "_DEFAULT"; // Is generated into the PreferenceInitializer. Actually, since we do have it in the initializer, passing it here again // as default value is just a safety measure if something went wrong and the property shouldn't be set. } - val javaDefaultValue = catalog.preferenceInitializerClassName + '.' + defaultName; - members += parameter.toMethod(parameter.formalParameterGetterName, returnType) [ - documentation = ''' - Gets the run-time value of formal parameter «parameter.name». The value - returned is either the default as defined in the check definition, or the - configured value, if existing. - - @param context - the context object used to determine the current project in - order to check if a configured value exists in a project scope - @return the run-time value of «parameter.name»'''; - val eObjectTypeRef = checkedTypeRef(parameter, typeof(EObject)); - if (eObjectTypeRef !== null) { - parameters += parameter.toParameter('context', eObjectTypeRef); + final String javaDefaultValue = checkGeneratorNaming.preferenceInitializerClassName(catalog) + "." + defaultName; + members.add(jvmTypesBuilder.toMethod(parameter, checkGeneratorNaming.formalParameterGetterName(parameter), returnType, (final JvmOperation it) -> { + final StringConcatenation builder = new StringConcatenation(); + builder.append("Gets the run-time value of formal parameter "); + builder.append(parameter.getName()); + builder.append(". The value"); + builder.newLineIfNotEmpty(); + builder.append("returned is either the default as defined in the check definition, or the"); + builder.newLine(); + builder.append("configured value, if existing."); + builder.newLine(); + builder.newLine(); + builder.append("@param context"); + builder.newLine(); + builder.append(" "); + builder.append("the context object used to determine the current project in"); + builder.newLine(); + builder.append(" "); + builder.append("order to check if a configured value exists in a project scope"); + builder.newLine(); + builder.append("@return the run-time value of "); + builder.append(parameter.getName()); + builder.append(""); + jvmTypesBuilder.setDocumentation(it, builder.toString()); + final JvmTypeReference eObjectTypeRef = checkedTypeRef(parameter, EObject.class); + if (eObjectTypeRef != null) { + it.getParameters().add(jvmTypesBuilder.toParameter(parameter, "context", eObjectTypeRef)); } - body = [append(''' - return checkConfigurationStoreService.getCheckConfigurationStore(context).«operation»("«parameterKey»", «javaDefaultValue»);''' - )]; - ]; + jvmTypesBuilder.setBody(it, (final ITreeAppendable appendable) -> appendable.append("return checkConfigurationStoreService.getCheckConfigurationStore(context)." + operation + "(\"" + parameterKey + "\", " + javaDefaultValue + ");")); + })); } // end if } // end for - members += check.toMethod('get' + check.name.toFirstUpper + 'Message', typeRef(typeof(String))) [ - documentation = CheckJvmModelInferrerUtil.GET_MESSAGE_DOCUMENTATION; + members.add(jvmTypesBuilder.toMethod(check, "get" + StringExtensions.toFirstUpper(check.getName()) + "Message", _typeReferenceBuilder.typeRef(String.class), (final JvmOperation it) -> { + jvmTypesBuilder.setDocumentation(it, CheckJvmModelInferrerUtil.GET_MESSAGE_DOCUMENTATION); // Generate one parameter "Object... bindings" - varArgs = true; - parameters += check.toParameter('bindings', addArrayTypeDimension(typeRef(typeof(Object)))); - body = [append(''' - return org.eclipse.osgi.util.NLS.bind("«Strings::convertToJavaString(check.message)»", bindings);''' - )]; + it.setVarArgs(true); + it.getParameters().add(jvmTypesBuilder.toParameter(check, "bindings", jvmTypesBuilder.addArrayTypeDimension(_typeReferenceBuilder.typeRef(Object.class)))); + jvmTypesBuilder.setBody(it, (final ITreeAppendable appendable) -> appendable.append("return org.eclipse.osgi.util.NLS.bind(\"" + Strings.convertToJavaString(check.getMessage()) + "\", bindings);")); // TODO (minor): how to get NLS into the imports? - ]; - val severityType = checkedTypeRef(check, typeof(SeverityKind)); - if (severityType !== null) { - members += check.toMethod('get' + check.name.toFirstUpper + 'SeverityKind', severityType) [ - documentation = ''' - Gets the {@link SeverityKind severity kind} of check - «check.label». The severity kind returned is either the - default ({@code «check.defaultSeverity.name()»}), as is set in the check definition, or the - configured value, if existing. - - @param context - the context object used to determine the current project in - order to check if a configured value exists in a project scope - @return the severity kind of this check: returns the default («check.defaultSeverity.name()») if - no configuration for this check was found, else the configured - value looked up in the configuration store'''; - val eObjectTypeRef = checkedTypeRef(check, typeof(EObject)); - if (eObjectTypeRef !== null) { - parameters += check.toParameter('context', eObjectTypeRef); + })); + final JvmTypeReference severityType = checkedTypeRef(check, SeverityKind.class); + if (severityType != null) { + members.add(jvmTypesBuilder.toMethod(check, "get" + StringExtensions.toFirstUpper(check.getName()) + "SeverityKind", severityType, (final JvmOperation it) -> { + final StringConcatenation builder = new StringConcatenation(); + builder.append("Gets the {@link SeverityKind severity kind} of check"); + builder.newLine(); + builder.append(""); + builder.append(check.getLabel()); + builder.append(". The severity kind returned is either the"); + builder.newLineIfNotEmpty(); + builder.append("default ({@code "); + builder.append(check.getDefaultSeverity().name()); + builder.append("}), as is set in the check definition, or the"); + builder.newLineIfNotEmpty(); + builder.append("configured value, if existing."); + builder.newLine(); + builder.newLine(); + builder.append("@param context"); + builder.newLine(); + builder.append(" "); + builder.append("the context object used to determine the current project in"); + builder.newLine(); + builder.append(" "); + builder.append("order to check if a configured value exists in a project scope"); + builder.newLine(); + builder.append("@return the severity kind of this check: returns the default ("); + builder.append(check.getDefaultSeverity().name()); + builder.append(") if"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("no configuration for this check was found, else the configured"); + builder.newLine(); + builder.append(" "); + builder.append("value looked up in the configuration store"); + jvmTypesBuilder.setDocumentation(it, builder.toString()); + final JvmTypeReference eObjectTypeRef = checkedTypeRef(check, EObject.class); + if (eObjectTypeRef != null) { + it.getParameters().add(jvmTypesBuilder.toParameter(check, "context", eObjectTypeRef)); } - body = [append(''' - final int result = checkConfigurationStoreService.getCheckConfigurationStore(context).getInt("«CheckPropertiesGenerator::checkSeverityKey(check)»", «check.defaultSeverity.value»); - return SeverityKind.values()[result];''' - )]; - ]; + jvmTypesBuilder.setBody(it, (final ITreeAppendable appendable) -> { + final StringConcatenation bodyBuilder = new StringConcatenation(); + bodyBuilder.append("final int result = checkConfigurationStoreService.getCheckConfigurationStore(context).getInt(\""); + bodyBuilder.append(CheckPropertiesGenerator.checkSeverityKey(check)); + bodyBuilder.append("\", "); + bodyBuilder.append(check.getDefaultSeverity().getValue()); + bodyBuilder.append(");"); + bodyBuilder.newLineIfNotEmpty(); + bodyBuilder.append("return SeverityKind.values()[result];"); + appendable.append(bodyBuilder); + }); + })); } return members; } + // CHECKSTYLE:CHECK-ON LambdaBodyLength // PreferenceInitializer. - private def Iterable createFormalParameterFields (CheckCatalog catalog) { + private Iterable createFormalParameterFields(final CheckCatalog catalog) { // For each formal parameter, create a public static final field with a unique name derived from the formal parameter and // set it to its right-hand side expression. We let Java evaluate this! - val allChecks = catalog.checks + catalog.categories.map(cat|cat.checks).flatten; - val List result = Lists::newArrayList(); - for (Check c : allChecks) { - for (FormalParameter parameter : c.formalParameters) { - if (parameter.type !== null && parameter.right !== null) { - val String defaultName = parameter.formalParameterGetterName.splitCamelCase.toUpperCase + '_DEFAULT'; - result += parameter.toField(defaultName, parameter.type) [ - visibility = JvmVisibility::PUBLIC; - final = true; - static = true; - initializer = parameter.right; - ]; + final List checks = catalog.getChecks(); + final Iterable flattenedCategoryChecks = Iterables.concat(ListExtensions.>map(catalog.getCategories(), (final Category cat) -> cat.getChecks())); + final Iterable allChecks = Iterables.concat(checks, flattenedCategoryChecks); + final List result = Lists.newArrayList(); + for (final Check c : allChecks) { + for (final FormalParameter parameter : c.getFormalParameters()) { + if (parameter.getType() != null && parameter.getRight() != null) { + final String defaultName = CheckGeneratorExtensions.splitCamelCase(checkGeneratorNaming.formalParameterGetterName(parameter)).toUpperCase() + "_DEFAULT"; + result.add(jvmTypesBuilder.toField(parameter, defaultName, parameter.getType(), (final JvmField it) -> { + it.setVisibility(JvmVisibility.PUBLIC); + it.setFinal(true); + it.setStatic(true); + jvmTypesBuilder.setInitializer(it, parameter.getRight()); + })); } - } } return result; } - private def Iterable createPreferenceInitializerMethods(CheckCatalog catalog) { - val JvmTypeReference prefStore = checkedTypeRef(catalog, typeof (IEclipsePreferences)); - val List result = Lists::newArrayList(); - - if (prefStore !== null) { - result += catalog.toMethod('initializeDefaultPreferences', typeRef('void')) [ - annotations += createAnnotation(checkedTypeRef(catalog, typeof(Override)), []); - visibility = JvmVisibility::PUBLIC; - body = [append(''' - IEclipsePreferences preferences = org.eclipse.core.runtime.preferences.InstanceScope.INSTANCE.getNode(RUNTIME_NODE_NAME); - - initializeSeverities(preferences); - initializeFormalParameters(preferences);''')]; - ]; - val allChecks = catalog.checks + catalog.categories.map(cat|cat.checks).flatten; - result += catalog.toMethod('initializeSeverities', typeRef('void')) [ - visibility = JvmVisibility::PRIVATE; - parameters += catalog.toParameter('preferences', prefStore); - body = [append('''«FOR c:allChecks» - preferences.putInt("«CheckPropertiesGenerator::checkSeverityKey(c)»", «c.defaultSeverity.value»); - «ENDFOR»''')]; - ]; - result += catalog.toMethod('initializeFormalParameters', typeRef('void')) [ - visibility = JvmVisibility::PRIVATE; - parameters += catalog.toParameter('preferences', prefStore.cloneWithProxies); - body = [ - for (Check c : allChecks) { - for (FormalParameter parameter : c.formalParameters) { - if (parameter.right !== null) { - val String key = CheckPropertiesGenerator::parameterKey(parameter, c); - val String defaultFieldName = parameter.formalParameterGetterName.splitCamelCase.toUpperCase + '_DEFAULT'; - val JvmTypeReference jvmType = parameter.type; - val String typeName = jvmType.qualifiedName; - if (typeName !== null && typeName.startsWith("java.util.List<")) { + // CHECKSTYLE:CHECK-OFF LambdaBodyLength the model-inference closures mirror the Xtext JvmTypesBuilder API and are kept whole + private Iterable createPreferenceInitializerMethods(final CheckCatalog catalog) { + final JvmTypeReference prefStore = checkedTypeRef(catalog, IEclipsePreferences.class); + final List result = Lists.newArrayList(); + + if (prefStore != null) { + result.add(jvmTypesBuilder.toMethod(catalog, "initializeDefaultPreferences", _typeReferenceBuilder.typeRef("void"), (final JvmOperation it) -> { + Iterables.addAll(it.getAnnotations(), createAnnotation(checkedTypeRef(catalog, Override.class), (final JvmAnnotationReference it1) -> { + })); + it.setVisibility(JvmVisibility.PUBLIC); + jvmTypesBuilder.setBody(it, (final ITreeAppendable appendable) -> appendable.append("IEclipsePreferences preferences = org.eclipse.core.runtime.preferences.InstanceScope.INSTANCE.getNode(RUNTIME_NODE_NAME);\n\ninitializeSeverities(preferences);\ninitializeFormalParameters(preferences);")); + })); + final List checks = catalog.getChecks(); + final Iterable flattenedCategoryChecks = Iterables.concat(ListExtensions.>map(catalog.getCategories(), (final Category cat) -> cat.getChecks())); + final Iterable allChecks = Iterables.concat(checks, flattenedCategoryChecks); + result.add(jvmTypesBuilder.toMethod(catalog, "initializeSeverities", _typeReferenceBuilder.typeRef("void"), (final JvmOperation it) -> { + it.setVisibility(JvmVisibility.PRIVATE); + it.getParameters().add(jvmTypesBuilder.toParameter(catalog, "preferences", prefStore)); + jvmTypesBuilder.setBody(it, (final ITreeAppendable appendable) -> { + final StringConcatenation builder = new StringConcatenation(); + for (final Check c : allChecks) { + builder.newLineIfNotEmpty(); + builder.append("preferences.putInt(\""); + builder.append(CheckPropertiesGenerator.checkSeverityKey(c)); + builder.append("\", "); + builder.append(c.getDefaultSeverity().getValue()); + builder.append(");"); + builder.newLineIfNotEmpty(); + } + appendable.append(builder); + }); + })); + result.add(jvmTypesBuilder.toMethod(catalog, "initializeFormalParameters", _typeReferenceBuilder.typeRef("void"), (final JvmOperation it) -> { + it.setVisibility(JvmVisibility.PRIVATE); + it.getParameters().add(jvmTypesBuilder.toParameter(catalog, "preferences", jvmTypesBuilder.cloneWithProxies(prefStore))); + jvmTypesBuilder.setBody(it, (final ITreeAppendable appendable) -> { + for (final Check c : allChecks) { + for (final FormalParameter parameter : c.getFormalParameters()) { + if (parameter.getRight() != null) { + final String key = CheckPropertiesGenerator.parameterKey(parameter, c); + final String defaultFieldName = CheckGeneratorExtensions.splitCamelCase(checkGeneratorNaming.formalParameterGetterName(parameter)).toUpperCase() + "_DEFAULT"; + final JvmTypeReference jvmType = parameter.getType(); + final String typeName = jvmType.getQualifiedName(); + if (typeName != null && typeName.startsWith("java.util.List<")) { // Marshal lists. - val args = (jvmType as JvmParameterizedTypeReference).arguments; - if (args !== null && args.size == 1) { - val baseTypeName = args.head.simpleName; - append('''preferences.put("«key»", com.avaloq.tools.ddk.check.runtime.configuration.CheckPreferencesHelper.marshal«baseTypeName»s(«defaultFieldName»)); - '''); + final List args = ((JvmParameterizedTypeReference) jvmType).getArguments(); + if (args != null && args.size() == 1) { + final String baseTypeName = IterableExtensions.head(args).getSimpleName(); + final StringConcatenation builder = new StringConcatenation(); + builder.append("preferences.put(\""); + builder.append(key); + builder.append("\", com.avaloq.tools.ddk.check.runtime.configuration.CheckPreferencesHelper.marshal"); + builder.append(baseTypeName); + builder.append("s("); + builder.append(defaultFieldName); + builder.append("));"); + builder.newLineIfNotEmpty(); + appendable.append(builder); } else { - append('''// Found «key» with «typeName» - '''); + final StringConcatenation builder = new StringConcatenation(); + builder.append("// Found "); + builder.append(key); + builder.append(" with "); + builder.append(typeName); + builder.newLineIfNotEmpty(); + appendable.append(builder); } } else { - val String operation = switch typeName { - case 'java.lang.Boolean' : 'putBoolean' - case 'boolean' : 'putBoolean' - case 'java.lang.Integer' : 'putInt' - case 'int' : 'putInt' - default : 'put' - }; - append('''preferences.«operation»("«key»", «defaultFieldName»); - '''); + final String operation; + if (typeName != null) { + operation = switch (typeName) { + case "java.lang.Boolean" -> "putBoolean"; + case "boolean" -> "putBoolean"; + case "java.lang.Integer" -> "putInt"; + case "int" -> "putInt"; + default -> "put"; + }; + } else { + operation = "put"; + } + final StringConcatenation builder = new StringConcatenation(); + builder.append("preferences."); + builder.append(operation); + builder.append("(\""); + builder.append(key); + builder.append("\", "); + builder.append(defaultFieldName); + builder.append(");"); + builder.newLineIfNotEmpty(); + appendable.append(builder); } } } } - ]; - ]; + }); + })); } return result; - } + // CHECKSTYLE:CHECK-ON LambdaBodyLength + // CHECKSTYLE:CONSTANTS-ON - private def Iterable createAnnotation (JvmTypeReference typeRef, Procedure1 initializer) { - if (typeRef === null) { - return Collections::emptyList; + private Iterable createAnnotation(final JvmTypeReference typeRef, final Procedure1 initializer) { + if (typeRef == null) { + return Collections.emptyList(); } - val annotation = typesFactory.createJvmAnnotationReference() - annotation.annotation = typeRef.type as JvmAnnotationType - Objects.requireNonNull(initializer, "Initializer is null").apply(annotation) + final JvmAnnotationReference annotation = typesFactory.createJvmAnnotationReference(); + annotation.setAnnotation((JvmAnnotationType) typeRef.getType()); + Objects.requireNonNull(initializer, "Initializer is null").apply(annotation); - return Collections::singletonList(annotation) + return Collections.singletonList(annotation); } // Error handling etc. - private def createError (String message, EObject context, EStructuralFeature feature) { - val Resource rsc = context.eResource; - if (rsc !== null) { - var f = feature; - if (f === null) { + private void createError(final String message, final EObject context, final EStructuralFeature feature) { + final Resource rsc = context.eResource(); + if (rsc != null) { + EStructuralFeature f = feature; + if (f == null) { f = locationInFileProvider.getIdentifierFeature(context); } - rsc.errors += new EObjectDiagnosticImpl(Severity::ERROR, IssueCodes::INFERRER_ERROR, "Check compiler: " + message, context, f, -1, null) + rsc.getErrors().add(new EObjectDiagnosticImpl(Severity.ERROR, IssueCodes.INFERRER_ERROR, "Check compiler: " + message, context, f, -1, null)); } } - private def createTypeNotFoundError(String name, EObject context) { + private void createTypeNotFoundError(final String name, final EObject context) { createError("Type " + name + " not found; check project setup (missing required bundle?)", context, null); } - private def JvmTypeReference checkedTypeRef(EObject context, Class clazz) { - if (clazz === null) { - createTypeNotFoundError ("", context); + private JvmTypeReference checkedTypeRef(final EObject context, final Class clazz) { + if (clazz == null) { + createTypeNotFoundError("", context); return null; } - val result = typeRef(clazz); - if (result === null || result.type === null) { - createTypeNotFoundError(clazz.name, context); + final JvmTypeReference result = _typeReferenceBuilder.typeRef(clazz); + if (result == null || result.getType() == null) { + createTypeNotFoundError(clazz.getName(), context); return null; } return result; } + + @Override + public void infer(final EObject catalog, final IJvmDeclaredTypeAcceptor acceptor, final boolean preIndexingPhase) { + if (catalog instanceof CheckCatalog checkCatalog) { + _infer(checkCatalog, acceptor, preIndexingPhase); + } else if (catalog != null) { + _infer(catalog, acceptor, preIndexingPhase); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.asList(catalog, acceptor, preIndexingPhase).toString()); + } + } } diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/scoping/CheckScopeProvider.java b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/scoping/CheckScopeProvider.java index 081f0815e9..0e8eac8496 100644 --- a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/scoping/CheckScopeProvider.java +++ b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/scoping/CheckScopeProvider.java @@ -8,171 +8,217 @@ * Contributors: * Avaloq Group AG - initial API and implementation *******************************************************************************/ -package com.avaloq.tools.ddk.check.scoping - -import com.avaloq.tools.ddk.check.check.CheckCatalog -import com.avaloq.tools.ddk.check.check.CheckPackage -import com.avaloq.tools.ddk.check.check.Context -import com.avaloq.tools.ddk.check.check.XIssueExpression -import com.avaloq.tools.ddk.check.naming.CheckDeclarativeQualifiedNameProvider -import com.google.common.base.Predicates -import com.google.common.collect.Collections2 -import com.google.common.collect.Iterables -import com.google.common.collect.Sets -import com.google.inject.Inject -import org.eclipse.emf.common.util.URI -import org.eclipse.emf.ecore.EClass -import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.EPackage -import org.eclipse.emf.ecore.EReference -import org.eclipse.emf.ecore.EcorePackage -import org.eclipse.emf.ecore.resource.Resource -import org.eclipse.emf.ecore.util.EcoreUtil -import org.eclipse.xtext.EcoreUtil2 -import org.eclipse.xtext.IGrammarAccess -import org.eclipse.xtext.common.types.JvmType -import org.eclipse.xtext.naming.IQualifiedNameConverter -import org.eclipse.xtext.naming.QualifiedName -import org.eclipse.xtext.resource.EObjectDescription -import org.eclipse.xtext.resource.IResourceServiceProvider -import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider -import org.eclipse.xtext.scoping.IGlobalScopeProvider -import org.eclipse.xtext.scoping.IScope -import org.eclipse.xtext.scoping.impl.MapBasedScope -import org.eclipse.xtext.scoping.impl.SimpleScope -import org.eclipse.xtext.xbase.annotations.typesystem.XbaseWithAnnotationsBatchScopeProvider -import org.eclipse.xtext.xbase.typesystem.IBatchTypeResolver - -class CheckScopeProvider extends XbaseWithAnnotationsBatchScopeProvider { - - @Inject CheckDeclarativeQualifiedNameProvider checkQualifiedNameProvider - @Inject IQualifiedNameConverter qualifiedNameConverter - @Inject IBatchTypeResolver typeResolver; - @Inject IGlobalScopeProvider globalScopeProvider - @Inject ResourceDescriptionsProvider descriptionsProvider +package com.avaloq.tools.ddk.check.scoping; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.xtext.EcoreUtil2; +import org.eclipse.xtext.Grammar; +import org.eclipse.xtext.IGrammarAccess; +import org.eclipse.xtext.common.types.JvmType; +import org.eclipse.xtext.common.types.JvmTypeReference; +import org.eclipse.xtext.naming.IQualifiedNameConverter; +import org.eclipse.xtext.naming.QualifiedName; +import org.eclipse.xtext.resource.EObjectDescription; +import org.eclipse.xtext.resource.IEObjectDescription; +import org.eclipse.xtext.resource.IResourceServiceProvider; +import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider; +import org.eclipse.xtext.scoping.IGlobalScopeProvider; +import org.eclipse.xtext.scoping.IScope; +import org.eclipse.xtext.scoping.impl.MapBasedScope; +import org.eclipse.xtext.scoping.impl.SimpleScope; +import org.eclipse.xtext.xbase.annotations.typesystem.XbaseWithAnnotationsBatchScopeProvider; +import org.eclipse.xtext.xbase.lib.IterableExtensions; +import org.eclipse.xtext.xbase.lib.ListExtensions; +import org.eclipse.xtext.xbase.typesystem.IBatchTypeResolver; + +import com.avaloq.tools.ddk.check.check.Check; +import com.avaloq.tools.ddk.check.check.CheckCatalog; +import com.avaloq.tools.ddk.check.check.CheckPackage; +import com.avaloq.tools.ddk.check.check.Context; +import com.avaloq.tools.ddk.check.check.XIssueExpression; +import com.avaloq.tools.ddk.check.naming.CheckDeclarativeQualifiedNameProvider; +import com.google.common.base.Predicates; +import com.google.common.collect.Collections2; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; +import com.google.inject.Inject; + + +@SuppressWarnings({"checkstyle:MethodName", "PMD.UnusedFormalParameter", "nls"}) +public class CheckScopeProvider extends XbaseWithAnnotationsBatchScopeProvider { + + @Inject + private CheckDeclarativeQualifiedNameProvider checkQualifiedNameProvider; + + @Inject + private IQualifiedNameConverter qualifiedNameConverter; + + @Inject + private IBatchTypeResolver typeResolver; + + @Inject + private IGlobalScopeProvider globalScopeProvider; + + @Inject + private ResourceDescriptionsProvider descriptionsProvider; // Use dispatch definitions instead of a switch statement since // https://bugs.eclipse.org/bugs/show_bug.cgi?id=368263 // will otherwise cause the builder to fail during linking. - override IScope getScope(EObject context, EReference reference) { - val res = scope(context, reference) - if (res !== null) res else super.getScope(context, reference) + @Override + public IScope getScope(final EObject context, final EReference reference) { + final IScope res = scope(context, reference); + if (res != null) { + return res; + } else { + return super.getScope(context, reference); + } } - def dispatch IScope scope(XIssueExpression context, EReference reference) { - if (reference == CheckPackage.Literals::XISSUE_EXPRESSION__MARKER_FEATURE) { - var jvmTypeRef = - if (context.markerObject !== null) - typeResolver.resolveTypes(context.markerObject).getActualType(context.markerObject).toTypeReference - else - EcoreUtil2::getContainerOfType(context, typeof(Context)).contextVariable.type; - - if (jvmTypeRef !== null) { - val eClass = context.classForJvmType(jvmTypeRef.type); - if (eClass !== null) { - var features = eClass.EAllStructuralFeatures - val descriptions = Collections2::transform(features, [f | EObjectDescription::create(QualifiedName::create(f.name), f)]) - return MapBasedScope::createScope(IScope::NULLSCOPE, descriptions); + protected IScope _scope(final XIssueExpression context, final EReference reference) { + if (Objects.equals(reference, CheckPackage.Literals.XISSUE_EXPRESSION__MARKER_FEATURE)) { + JvmTypeReference jvmTypeRef; + if (context.getMarkerObject() != null) { + jvmTypeRef = typeResolver.resolveTypes(context.getMarkerObject()).getActualType(context.getMarkerObject()).toTypeReference(); + } else { + jvmTypeRef = EcoreUtil2. getContainerOfType(context, Context.class).getContextVariable().getType(); + } + + if (jvmTypeRef != null) { + final EClass eClass = classForJvmType(context, jvmTypeRef.getType()); + if (eClass != null) { + final EList features = eClass.getEAllStructuralFeatures(); + final Collection descriptions = Collections2.transform(features, (final EStructuralFeature f) -> EObjectDescription.create(QualifiedName.create(f.getName()), f)); + return MapBasedScope.createScope(IScope.NULLSCOPE, descriptions); } else { - return IScope::NULLSCOPE; + return IScope.NULLSCOPE; } - } else { - return IScope::NULLSCOPE; + } else { + return IScope.NULLSCOPE; } - } else if (reference == CheckPackage.Literals::XISSUE_EXPRESSION__CHECK) { + } else if (Objects.equals(reference, CheckPackage.Literals.XISSUE_EXPRESSION__CHECK)) { // Make sure that only Checks of the current model can be referenced, and if the CheckCatalog includes // another CheckCatalog, then use that parent as parent scope - val catalog = EcoreUtil2::getContainerOfType(context, typeof(CheckCatalog)) - val checks = catalog.allChecks.filter(c|c.name !== null).toList + final CheckCatalog catalog = EcoreUtil2. getContainerOfType(context, CheckCatalog.class); + final List checks = IterableExtensions. toList(IterableExtensions. filter(catalog.getAllChecks(), c -> c.getName() != null)); - val descriptions = Collections2::transform(checks, [c|EObjectDescription::create(QualifiedName::create(c.name), c)]) + final Collection descriptions = Collections2.transform(checks, (final Check c) -> EObjectDescription.create(QualifiedName.create(c.getName()), c)); // Determine the parent scope; use NULLSCOPE if no included CheckCatalog is defined (or if it cannot be resolved) - val parentScope = IScope::NULLSCOPE + final IScope parentScope = IScope.NULLSCOPE; - return MapBasedScope::createScope(parentScope, Iterables::filter(descriptions, Predicates::notNull)); + return MapBasedScope.createScope(parentScope, Iterables.filter(descriptions, Predicates.notNull())); } + return null; } - def dispatch IScope scope(CheckCatalog context, EReference reference) { - if (reference == CheckPackage.Literals::CHECK_CATALOG__GRAMMAR) { - val reg = IResourceServiceProvider.Registry::INSTANCE - val descriptions = Collections2::transform(reg.extensionToFactoryMap.keySet, - [e | { - val dummyUri = URI::createURI("foo:/foo." + e) - try { - val g = reg.getResourceServiceProvider(dummyUri).get(typeof(IGrammarAccess)).grammar - return EObjectDescription::create(qualifiedNameConverter.toQualifiedName(g.name), g) - } catch (Exception ex) {} - }] - ) + protected IScope _scope(final CheckCatalog context, final EReference reference) { + if (Objects.equals(reference, CheckPackage.Literals.CHECK_CATALOG__GRAMMAR)) { + final IResourceServiceProvider.Registry reg = IResourceServiceProvider.Registry.INSTANCE; + // CHECKSTYLE:CHECK-OFF IllegalCatch + final Collection descriptions = Collections2.transform(reg.getExtensionToFactoryMap().keySet(), (final String e) -> { + final URI dummyUri = URI.createURI("foo:/foo." + e); + try { + final Grammar g = reg.getResourceServiceProvider(dummyUri).get(IGrammarAccess.class).getGrammar(); + return EObjectDescription.create(qualifiedNameConverter.toQualifiedName(g.getName()), g); + } catch (Exception ex) { + return null; + } + }); + // CHECKSTYLE:CHECK-ON IllegalCatch // We look first in the workspace for a grammar and then in the registry for a registered grammar - val parentScope = MapBasedScope::createScope(IScope::NULLSCOPE, Iterables::filter(descriptions, Predicates::notNull)); - return parentScope; - //val grammarScope = new DelegatingScope(parentScope); - //grammarScope.setDelegate(super.getScope(context, reference)); - //return grammarScope; - } else if (reference == CheckPackage.Literals::XISSUE_EXPRESSION__CHECK) { - val descriptions = context.allChecks.map(c|EObjectDescription::create(checkQualifiedNameProvider.getFullyQualifiedName(c), c)) - return new SimpleScope(super.getScope(context, reference), descriptions) + return MapBasedScope.createScope(IScope.NULLSCOPE, Iterables.filter(descriptions, Predicates.notNull())); + } else if (Objects.equals(reference, CheckPackage.Literals.XISSUE_EXPRESSION__CHECK)) { + final List descriptions = ListExtensions.map(context.getAllChecks(), (final Check c) -> EObjectDescription.create(checkQualifiedNameProvider.getFullyQualifiedName(c), c)); + return new SimpleScope(super.getScope(context, reference), descriptions); } + return null; } // default implementation will throw an illegal argument exception - def dispatch IScope scope(EObject context, EReference reference) { - return null + protected IScope _scope(final EObject context, final EReference reference) { + return null; } - def EClass classForJvmType(EObject context, JvmType jvmType) { - if (jvmType !== null && !jvmType.eIsProxy) { - val qualifiedName = jvmType.getQualifiedName(); - val qualifiedPackageName = qualifiedName.substring(0, qualifiedName.lastIndexOf(".")); - val packageName = qualifiedPackageName.substring(qualifiedPackageName.lastIndexOf(".") + 1); - val ePackage = getEPackage(context.eResource, packageName); - if (ePackage !== null) { - val eClassifier = (EcoreUtil::resolve(ePackage, context) as EPackage).getEClassifier(jvmType.simpleName) + public IScope scope(final EObject context, final EReference reference) { + if (context instanceof CheckCatalog) { + return _scope((CheckCatalog) context, reference); + } else if (context instanceof XIssueExpression) { + return _scope((XIssueExpression) context, reference); + } else if (context != null) { + return _scope(context, reference); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + + Arrays. asList(context, reference).toString()); + } + } + + public EClass classForJvmType(final EObject context, final JvmType jvmType) { + if (jvmType != null && !jvmType.eIsProxy()) { + final String qualifiedName = jvmType.getQualifiedName(); + final String qualifiedPackageName = qualifiedName.substring(0, qualifiedName.lastIndexOf('.')); + final String packageName = qualifiedPackageName.substring(qualifiedPackageName.lastIndexOf('.') + 1); + final EPackage ePackage = getEPackage(context.eResource(), packageName); + if (ePackage != null) { + final EClassifier eClassifier = ((EPackage) EcoreUtil.resolve(ePackage, context)).getEClassifier(jvmType.getSimpleName()); if (eClassifier instanceof EClass) { - return eClassifier + return (EClass) eClassifier; } } } - return null + return null; } - def EPackage getEPackage(Resource context, String name) { + public EPackage getEPackage(final Resource context, final String name) { // not using for-each loop, as it could result in a ConcurrentModificationException when a resource is demand-loaded - val resources = context.resourceSet.resources - for (var i = 0; i < resources.size; i++) { - val resource = resources.get(i) - for (obj : resource.contents) { - if (obj instanceof EPackage && (obj as EPackage).name == name) - return obj as EPackage + final EList resources = context.getResourceSet().getResources(); + for (int i = 0; i < resources.size(); i++) { + final Resource resource = resources.get(i); + for (final EObject obj : resource.getContents()) { + if (obj instanceof EPackage && Objects.equals(((EPackage) obj).getName(), name)) { + return (EPackage) obj; + } } } - val desc = globalScopeProvider.getScope(context, EcorePackage.Literals.EPACKAGE__ESUPER_PACKAGE, null).getSingleElement(QualifiedName.create(name)) - if (desc !== null) { - return desc.EObjectOrProxy as EPackage + final IEObjectDescription desc = globalScopeProvider.getScope(context, EcorePackage.Literals.EPACKAGE__ESUPER_PACKAGE, null).getSingleElement(QualifiedName.create(name)); + if (desc != null) { + return (EPackage) desc.getEObjectOrProxy(); } - val descs = descriptionsProvider.getResourceDescriptions(context).getExportedObjects(EcorePackage.Literals.EPACKAGE, QualifiedName.create(name), false).iterator - if (descs.hasNext) { - val pkg = EcoreUtil.resolve(descs.next.EObjectOrProxy, context) + final Iterator descs = descriptionsProvider.getResourceDescriptions(context).getExportedObjects(EcorePackage.Literals.EPACKAGE, QualifiedName.create(name), false).iterator(); + if (descs.hasNext()) { + final EObject pkg = EcoreUtil.resolve(descs.next().getEObjectOrProxy(), context); // this filtering only appears to be necessary when executing the unit test BugAig830 in Jenkins (see https://jira.sys.net/browse/ACF-8758) - if (!pkg.eIsProxy) - return pkg as EPackage + if (!pkg.eIsProxy()) { + return (EPackage) pkg; + } } - for (nsUri : Sets.newHashSet(EPackage.Registry.INSTANCE.keySet)) { - val ePackage = EPackage.Registry.INSTANCE.getEPackage(nsUri) - if (ePackage.name == name) { - return ePackage + for (final String nsUri : Sets.newHashSet(EPackage.Registry.INSTANCE.keySet())) { + final EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsUri); + if (Objects.equals(ePackage.getName(), name)) { + return ePackage; } } - return null + return null; } - //todo: scoping for the check implementation (e.g. the parameters are not visible) - - //todo: scope the allowed imports! + // todo: scoping for the check implementation (e.g. the parameters are not visible) + // todo: scope the allowed imports! } - diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/typing/CheckTypeComputer.java b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/typing/CheckTypeComputer.java index f42358d484..90d6317ce3 100644 --- a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/typing/CheckTypeComputer.java +++ b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/typing/CheckTypeComputer.java @@ -8,53 +8,57 @@ * Contributors: * Avaloq Group AG - initial API and implementation *******************************************************************************/ -package com.avaloq.tools.ddk.check.typing +package com.avaloq.tools.ddk.check.typing; -import org.eclipse.xtext.xbase.annotations.typesystem.XbaseWithAnnotationsTypeComputer -import org.eclipse.xtext.xbase.typesystem.computation.ITypeComputationState -import org.eclipse.xtext.xbase.XExpression -import com.avaloq.tools.ddk.check.check.XIssueExpression -import com.avaloq.tools.ddk.check.check.XGuardExpression -import org.eclipse.emf.ecore.EObject -import com.avaloq.tools.ddk.check.check.FormalParameter -import org.eclipse.xtext.xbase.XListLiteral +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.xbase.XExpression; +import org.eclipse.xtext.xbase.XListLiteral; +import org.eclipse.xtext.xbase.annotations.typesystem.XbaseWithAnnotationsTypeComputer; +import org.eclipse.xtext.xbase.typesystem.computation.ITypeComputationState; -class CheckTypeComputer extends XbaseWithAnnotationsTypeComputer { +import com.avaloq.tools.ddk.check.check.FormalParameter; +import com.avaloq.tools.ddk.check.check.XGuardExpression; +import com.avaloq.tools.ddk.check.check.XIssueExpression; - override computeTypes(XExpression expression, ITypeComputationState state) { + +@SuppressWarnings("checkstyle:MethodName") +public class CheckTypeComputer extends XbaseWithAnnotationsTypeComputer { + + @Override + public void computeTypes(final XExpression expression, final ITypeComputationState state) { if (expression instanceof XIssueExpression) { - _computeTypes(expression, state); + _computeTypes((XIssueExpression) expression, state); } else if (expression instanceof XGuardExpression) { - _computeTypes(expression, state); - } else if (expression.eContainer instanceof FormalParameter && expression instanceof XListLiteral && (expression as XListLiteral).elements.empty) { - super.computeTypes(expression, state.withExpectation(state.referenceOwner.toLightweightTypeReference((expression.eContainer as FormalParameter).type))); + _computeTypes((XGuardExpression) expression, state); + } else if (expression.eContainer() instanceof FormalParameter && expression instanceof XListLiteral && ((XListLiteral) expression).getElements().isEmpty()) { + super.computeTypes(expression, state.withExpectation(state.getReferenceOwner().toLightweightTypeReference(((FormalParameter) expression.eContainer()).getType()))); } else { super.computeTypes(expression, state); } } - protected def _computeTypes(XIssueExpression expression, ITypeComputationState state) { - if (expression.markerObject !== null) { - state.withExpectation(getTypeForName(typeof(EObject), state)).computeTypes(expression.markerObject); + protected void _computeTypes(final XIssueExpression expression, final ITypeComputationState state) { + if (expression.getMarkerObject() != null) { + state.withExpectation(getTypeForName(EObject.class, state)).computeTypes(expression.getMarkerObject()); } - if (expression.markerIndex !== null) { - state.withExpectation(getTypeForName(typeof(Integer), state)).computeTypes(expression.markerIndex); + if (expression.getMarkerIndex() != null) { + state.withExpectation(getTypeForName(Integer.class, state)).computeTypes(expression.getMarkerIndex()); } - if (expression.message !== null) { - state.withExpectation(getTypeForName(typeof(String), state)).computeTypes(expression.message); + if (expression.getMessage() != null) { + state.withExpectation(getTypeForName(String.class, state)).computeTypes(expression.getMessage()); } - for (p : expression.messageParameters) { - state.withExpectation(getTypeForName(typeof(Object), state)).computeTypes(p); + for (final XExpression p : expression.getMessageParameters()) { + state.withExpectation(getTypeForName(Object.class, state)).computeTypes(p); } - for (d : expression.issueData) { - state.withExpectation(getTypeForName(typeof(String), state)).computeTypes(d); + for (final XExpression d : expression.getIssueData()) { + state.withExpectation(getTypeForName(String.class, state)).computeTypes(d); } - state.acceptActualType(getPrimitiveVoid (state)); + state.acceptActualType(getPrimitiveVoid(state)); } - protected def _computeTypes(XGuardExpression expression, ITypeComputationState state) { - state.withExpectation(getTypeForName(typeof(Boolean), state)).computeTypes(expression.guard); - state.acceptActualType(getPrimitiveVoid (state)); + protected void _computeTypes(final XGuardExpression expression, final ITypeComputationState state) { + state.withExpectation(getTypeForName(Boolean.class, state)).computeTypes(expression.getGuard()); + state.acceptActualType(getPrimitiveVoid(state)); } } From 72b635ebe5330b765c7f9aa174d79de1a2a26df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dinis=20Ferreira?= Date: Mon, 6 Jul 2026 12:36:48 +0100 Subject: [PATCH 3/3] refactor: drop Xtend build infrastructure from check.core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The module is fully off Xtend: remove the xtend-gen source folder from build.properties and .classpath, the xtextBuilder/xtextNature from .project, and the xtend-gen directory marker — matching the other fully migrated modules. No Require-Bundle change needed (the module declares no Xtend bundles; xtend2.lib/xbase.lib resolve transitively for the StringConcatenation-based generators). Co-Authored-By: Claude Opus 4.8 --- com.avaloq.tools.ddk.check.core/.classpath | 1 - com.avaloq.tools.ddk.check.core/.project | 6 ------ com.avaloq.tools.ddk.check.core/build.properties | 3 +-- com.avaloq.tools.ddk.check.core/xtend-gen/.gitignore | 0 4 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 com.avaloq.tools.ddk.check.core/xtend-gen/.gitignore diff --git a/com.avaloq.tools.ddk.check.core/.classpath b/com.avaloq.tools.ddk.check.core/.classpath index 00aec70820..873687b273 100644 --- a/com.avaloq.tools.ddk.check.core/.classpath +++ b/com.avaloq.tools.ddk.check.core/.classpath @@ -6,7 +6,6 @@ - diff --git a/com.avaloq.tools.ddk.check.core/.project b/com.avaloq.tools.ddk.check.core/.project index 285f879570..f08f5317d5 100644 --- a/com.avaloq.tools.ddk.check.core/.project +++ b/com.avaloq.tools.ddk.check.core/.project @@ -20,11 +20,6 @@ - - org.eclipse.xtext.ui.shared.xtextBuilder - - - net.sf.eclipsecs.core.CheckstyleBuilder @@ -44,7 +39,6 @@ org.eclipse.jdt.core.javanature org.eclipse.pde.PluginNature - org.eclipse.xtext.ui.shared.xtextNature net.sourceforge.pmd.eclipse.plugin.pmdNature net.sf.eclipsecs.core.CheckstyleNature edu.umd.cs.findbugs.plugin.eclipse.findbugsNature diff --git a/com.avaloq.tools.ddk.check.core/build.properties b/com.avaloq.tools.ddk.check.core/build.properties index 7838757824..d69df83aa0 100644 --- a/com.avaloq.tools.ddk.check.core/build.properties +++ b/com.avaloq.tools.ddk.check.core/build.properties @@ -1,6 +1,5 @@ source.. = src/,\ - src-gen/,\ - xtend-gen/ + src-gen/ bin.includes = META-INF/,\ .,\ plugin.xml,\ diff --git a/com.avaloq.tools.ddk.check.core/xtend-gen/.gitignore b/com.avaloq.tools.ddk.check.core/xtend-gen/.gitignore deleted file mode 100644 index e69de29bb2..0000000000