From b19a7fbfc94758a833692fc7e134eb7c5b10af67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dinis=20Ferreira?= Date: Wed, 2 Sep 2026 20:57:58 +0200 Subject: [PATCH 1/3] refactor: migrate Xtend to Java - com.avaloq.tools.ddk.xtext.expression (1/2: rename sources) Pure `git mv` of the three generator sources from .xtend to .java with their content unchanged, so Git's rename detection permanently links the Java history to its Xtend past (git log --follow, git blame). This commit intentionally does not compile: the renamed files still hold Xtend syntax. The following commit rewrites them in place to Java 21. Co-Authored-By: Claude Fable 5.1 --- .../generator/{GenModelUtilX.xtend => GenModelUtilX.java} | 0 .../generator/{GeneratorUtilX.xtend => GeneratorUtilX.java} | 0 .../ddk/xtext/expression/generator/{Naming.xtend => Naming.java} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/{GenModelUtilX.xtend => GenModelUtilX.java} (100%) rename com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/{GeneratorUtilX.xtend => GeneratorUtilX.java} (100%) rename com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/{Naming.xtend => Naming.java} (100%) diff --git a/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GenModelUtilX.xtend b/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GenModelUtilX.java similarity index 100% rename from com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GenModelUtilX.xtend rename to com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GenModelUtilX.java diff --git a/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GeneratorUtilX.xtend b/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GeneratorUtilX.java similarity index 100% rename from com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GeneratorUtilX.xtend rename to com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GeneratorUtilX.java diff --git a/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/Naming.xtend b/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/Naming.java similarity index 100% rename from com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/Naming.xtend rename to com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/Naming.java From e5ebf47594f23d3830dc50754334c55292e6fbff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dinis=20Ferreira?= Date: Wed, 2 Sep 2026 21:06:36 +0200 Subject: [PATCH 2/3] refactor: migrate Xtend to Java - com.avaloq.tools.ddk.xtext.expression (2/2: translate to Java 21) In-place translation of the three renamed generator sources, faithful to the Xtend compiler's own xtend-gen output (fresh ground-truth build off the base commit, byte-identical to the reference). The module keeps ExpressionJvmModelInferrer.xtend, so the Xtend build infrastructure stays and there is no infrastructure commit. Public API is unchanged: the export and scope jvmmodel inferrers bind by dispatcher name (literalIdentifier(ENamedElement), instanceClassName(EClassifier)), by extension syntax (javaContributorComment, location, canContain, toJavaPackage) and by the Xtend property write `genModelUtil.resource = ...` (setResource(Resource)), all of which compile green with -amd. Notable per-file points: - GenModelUtilX: the dispatch cases keep their `_` prefix, their protected visibility, the Void overload as the null case and the reference's branch order (EClass, EStructuralFeature/EDataType, supertype, null). The dispatchers are Java 21 pattern switches; like xtend-gen they have no terminal IllegalArgumentException because the Void case makes them total. - GenModelUtilX: the ThreadLocal context field stays a ThreadLocal, and the `_literalIdentifier(EStructuralFeature)` literals branch keeps calling the public dispatcher (not the _ case) for the containing EClass, exactly as xtend-gen does. - GenModelUtilX: two private helpers replace xbase.lib calls with their exact semantics rather than the lossy idiomatic form - firstInterface() reproduces IterableExtensions.head returning null on an empty interface array (so an EPackage subclass declaring no interface still fails with an NPE, not an out-of-bounds), and toFirstUpper() reproduces StringExtensions.toFirstUpper including its null, empty and already-upper-case pass-throughs. - GenModelUtilX: Xtend `==`/`!=` on objects is translated to Objects.equals as xtend-gen did; the identity `!==` comparisons stay reference comparisons. The lazy IterableExtensions.filter feeding the Xcore candidate loop becomes a guard inside that loop, which iterates the same elements in the same order. - Naming / GeneratorUtilX: plain `+` concatenation preserved (no templates in these files); the Avaloq banner header is applied to all three files per the migration skill, replacing the two files' missing header. - One comment deviation: the vestigial `/*cached*/` markers the Xtend source carried on fifteen `def`s (remnants of former Xtend `create` methods, carrying no behaviour and already absent from xtend-gen) are dropped. Every other comment and Javadoc is preserved verbatim, and no Javadoc is invented - the only new Javadoc documents the two new private helpers. Co-Authored-By: Claude Fable 5.1 --- .../expression/generator/GenModelUtilX.java | 421 +++++++++++------- .../expression/generator/GeneratorUtilX.java | 51 ++- .../xtext/expression/generator/Naming.java | 21 +- 3 files changed, 306 insertions(+), 187 deletions(-) diff --git a/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GenModelUtilX.java b/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GenModelUtilX.java index ca1a2e3f5c..7a18e33d8d 100644 --- a/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GenModelUtilX.java +++ b/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GenModelUtilX.java @@ -1,38 +1,59 @@ -package com.avaloq.tools.ddk.xtext.expression.generator - -import com.google.inject.Inject -import org.eclipse.emf.codegen.ecore.genmodel.GenClass -import org.eclipse.emf.codegen.ecore.genmodel.GenDataType -import org.eclipse.emf.codegen.ecore.genmodel.GenModel -import org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage -import org.eclipse.emf.codegen.ecore.genmodel.GenPackage -import org.eclipse.emf.ecore.EClass -import org.eclipse.emf.ecore.EClassifier -import org.eclipse.emf.ecore.EDataType -import org.eclipse.emf.ecore.EModelElement -import org.eclipse.emf.ecore.ENamedElement -import org.eclipse.emf.ecore.EPackage -import org.eclipse.emf.ecore.EPackage.Registry -import org.eclipse.emf.ecore.EStructuralFeature -import org.eclipse.emf.ecore.impl.EPackageImpl -import org.eclipse.emf.ecore.resource.Resource -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl -import org.eclipse.emf.ecore.util.EcoreUtil -import org.eclipse.xtext.EcoreUtil2 -import org.eclipse.xtext.naming.QualifiedName -import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider -import org.eclipse.xtext.scoping.IGlobalScopeProvider -import org.eclipse.emf.ecore.EObject - -class GenModelUtilX { +/******************************************************************************* + * Copyright (c) 2016 Avaloq Group AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Avaloq Group AG - initial API and implementation + *******************************************************************************/ +package com.avaloq.tools.ddk.xtext.expression.generator; + +import java.util.Iterator; +import java.util.Objects; + +import org.eclipse.emf.codegen.ecore.genmodel.GenClass; +import org.eclipse.emf.codegen.ecore.genmodel.GenDataType; +import org.eclipse.emf.codegen.ecore.genmodel.GenModel; +import org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage; +import org.eclipse.emf.codegen.ecore.genmodel.GenPackage; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EModelElement; +import org.eclipse.emf.ecore.ENamedElement; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.impl.EPackageImpl; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.xtext.EcoreUtil2; +import org.eclipse.xtext.naming.QualifiedName; +import org.eclipse.xtext.resource.IEObjectDescription; +import org.eclipse.xtext.resource.IResourceDescriptions; +import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider; +import org.eclipse.xtext.scoping.IGlobalScopeProvider; +import org.eclipse.xtext.scoping.IScope; + +import com.google.inject.Inject; + + +@SuppressWarnings({"nls", "checkstyle:MethodName", "PMD.UnusedFormalParameter"}) +public class GenModelUtilX { + + private static final String DOES_NOT_EXIST = "DOES_NOT_EXIST"; @Inject - extension Naming + private Naming naming; @Inject - IGlobalScopeProvider globalScopeProvider + private IGlobalScopeProvider globalScopeProvider; @Inject - ResourceDescriptionsProvider resourceDescriptionsProvider + private ResourceDescriptionsProvider resourceDescriptionsProvider; /** * The current model resource used for GenPackage lookups. This utility is normally a Guice singleton, so @@ -40,47 +61,50 @@ class GenModelUtilX { * instance and would otherwise race on a single field. Storing the context per-thread isolates each worker's * lookups from the others. */ - val ThreadLocal context = new ThreadLocal() + private final ThreadLocal context = new ThreadLocal<>(); - def setResource(Resource context) { - this.context.set(context) + public void setResource(final Resource resource) { + context.set(resource); } - def Resource getContext() { - context.get() + public Resource getContext() { + return context.get(); } - def /*cached*/ String qualifiedPackageInterfaceName(EPackage it) { - val genPackage = genPackage() - if (genPackage !== null) - return genPackage.qualifiedPackageInterfaceName - else if (it.class != EPackageImpl) - return it.class.interfaces.head.name + public String qualifiedPackageInterfaceName(final EPackage ePackage) { + final GenPackage genPackage = genPackage(ePackage); + if (genPackage != null) { + return genPackage.getQualifiedPackageInterfaceName(); + } else if (!Objects.equals(ePackage.getClass(), EPackageImpl.class)) { + return firstInterface(ePackage.getClass()).getName(); + } // GenModel lookup failed (typically because the editor reconciler runs before the .genmodel index is // populated). Resolve via the EMF registry: the plugin-registered EPackage for this nsURI is a Java // class whose first interface is the generated *Package interface. - if (nsURI !== null) { - val registered = Registry.INSTANCE.getEPackage(nsURI) - if (registered !== null && registered !== it && registered.class != EPackageImpl) { - val interfaces = registered.class.interfaces - if (!interfaces.empty) - return interfaces.head.name + if (ePackage.getNsURI() != null) { + final EPackage registered = EPackage.Registry.INSTANCE.getEPackage(ePackage.getNsURI()); + if (registered != null && registered != ePackage && !Objects.equals(registered.getClass(), EPackageImpl.class)) { + final Class[] interfaces = registered.getClass().getInterfaces(); + if (interfaces.length != 0) { + return interfaces[0].getName(); + } } } - return null + return null; } - def String qualifiedSwitchClassName(EPackage it) { - val genPackage = genPackage() - if (genPackage !== null && genPackage.literalsInterface) - genPackage.utilitiesPackageName + "." + genPackage.switchClassName - else - qualifiedPackageInterfaceName().toJavaPackage + '.util.' + name.toFirstUpper + 'Switch' // heuristic + public String qualifiedSwitchClassName(final EPackage ePackage) { + final GenPackage genPackage = genPackage(ePackage); + if (genPackage != null && genPackage.isLiteralsInterface()) { + return genPackage.getUtilitiesPackageName() + "." + genPackage.getSwitchClassName(); + } else { + return naming.toJavaPackage(qualifiedPackageInterfaceName(ePackage)) + ".util." + toFirstUpper(ePackage.getName()) + "Switch"; // heuristic + } } - def /*cached*/ dispatch String literalIdentifier(EStructuralFeature it) { - val eClass = EContainingClass - if (eClass === null) { + protected String _literalIdentifier(final EStructuralFeature feature) { + final EClass eClass = feature.getEContainingClass(); + if (eClass == null) { // EStructuralFeature with no containing class - an unresolved EReference proxy. This happens during // editor reconciliation: a .scope file like `scope T#ref { ... }` records `s.reference` as a cross // reference whose scope is `T.eAllReferences`; if `T` itself is mid-resolve (e.g. the imported @@ -88,82 +112,93 @@ def String qualifiedSwitchClassName(EPackage it) { // container until linking completes. The generated body cannot reference this feature; emit a // sentinel so the body string parses and the reconciler can keep going. The next reconcile cycle // will retry against a fully-linked model and produce the correct literal. - return "DOES_NOT_EXIST" + return DOES_NOT_EXIST; + } + final EPackage ePackage = eClass.getEPackage(); + final GenPackage genPackage = genPackage(ePackage); + if (genPackage != null && genPackage.isLiteralsInterface()) { + return literalIdentifier(eClass) + "__" + format(feature.getName()).toUpperCase(); + } else { + return qualifiedPackageInterfaceName(ePackage) + ".eINSTANCE.get" + eClass.getName() + "_" + toFirstUpper(feature.getName()) + "()"; } - val ePackage = eClass.EPackage - val genPackage = ePackage.genPackage() - if (genPackage !== null && genPackage.literalsInterface) - eClass.literalIdentifier() + "__" + name.format().toUpperCase() - else - ePackage.qualifiedPackageInterfaceName() + ".eINSTANCE.get" + eClass.name + "_" + name.toFirstUpper() + "()" } - def /*cached*/ dispatch String literalIdentifier(EClass it) { - val genPackage = EPackage.genPackage() - if (genPackage !== null && genPackage.literalsInterface) - genPackage.qualifiedPackageInterfaceName + ".Literals." + name.format().toUpperCase() - else - EPackage.qualifiedPackageInterfaceName() + ".eINSTANCE.get" + name + "()" + protected String _literalIdentifier(final EClass eClass) { + final GenPackage genPackage = genPackage(eClass.getEPackage()); + if (genPackage != null && genPackage.isLiteralsInterface()) { + return genPackage.getQualifiedPackageInterfaceName() + ".Literals." + format(eClass.getName()).toUpperCase(); + } else { + return qualifiedPackageInterfaceName(eClass.getEPackage()) + ".eINSTANCE.get" + eClass.getName() + "()"; + } } // defined to simplify debugging generator problems - def /*cached*/ dispatch String literalIdentifier(ENamedElement it) { - "DOES_NOT_EXIST" + protected String _literalIdentifier(final ENamedElement element) { + return DOES_NOT_EXIST; } // defined to simplify debugging generator problems - def /*cached*/ dispatch String literalIdentifier(Void it) { - "DOES_NOT_EXIST" + protected String _literalIdentifier(final Void element) { + return DOES_NOT_EXIST; } // e.g. EcorePackage.ENAMED_ELEMENT - def /*cached*/ String classifierIdLiteral(EClass it) { - EPackage.qualifiedPackageInterfaceName() + "." + name.format().toUpperCase() - } - - def /*cached*/ dispatch String instanceClassName(Void it) { - "" - } - - def /*cached*/ dispatch String instanceClassName(EClassifier it) { - if (instanceClassName !== null) - return instanceClassName - val fromRegistry = instanceClassNameFromRegistry(it) - if (fromRegistry !== null) - return fromRegistry - if (name !== null) - return name - return EObject.name - } - - def /*cached*/ dispatch String instanceClassName(EDataType it) { - if (instanceClassName !== null) - return instanceClassName - val fromRegistry = instanceClassNameFromRegistry(it) - if (fromRegistry !== null) - return fromRegistry - val gdt = genDataType(it) - if (gdt !== null) - return gdt.qualifiedInstanceClassName + public String classifierIdLiteral(final EClass eClass) { + return qualifiedPackageInterfaceName(eClass.getEPackage()) + "." + format(eClass.getName()).toUpperCase(); + } + + protected String _instanceClassName(final Void classifier) { + return ""; + } + + protected String _instanceClassName(final EClassifier classifier) { + if (classifier.getInstanceClassName() != null) { + return classifier.getInstanceClassName(); + } + final String fromRegistry = instanceClassNameFromRegistry(classifier); + if (fromRegistry != null) { + return fromRegistry; + } + if (classifier.getName() != null) { + return classifier.getName(); + } + return EObject.class.getName(); + } + + protected String _instanceClassName(final EDataType dataType) { + if (dataType.getInstanceClassName() != null) { + return dataType.getInstanceClassName(); + } + final String fromRegistry = instanceClassNameFromRegistry(dataType); + if (fromRegistry != null) { + return fromRegistry; + } + final GenDataType gdt = genDataType(dataType); + if (gdt != null) { + return gdt.getQualifiedInstanceClassName(); + } // Unresolvable classifier (typically a proxy seen during editor reconciliation before linking // completes). Fall back to Object so the inferred Java method signature is well-formed and the // reconciler can keep going; the next reconcile cycle will retry with the resolved type. - return Object.name - } - - def /*cached*/ dispatch String instanceClassName(EClass it) { - if (instanceClassName !== null) - return instanceClassName - val fromRegistry = instanceClassNameFromRegistry(it) - if (fromRegistry !== null) - return fromRegistry - val gc = genClass(it) - if (gc !== null) - return gc.qualifiedInterfaceName + return Object.class.getName(); + } + + protected String _instanceClassName(final EClass eClass) { + if (eClass.getInstanceClassName() != null) { + return eClass.getInstanceClassName(); + } + final String fromRegistry = instanceClassNameFromRegistry(eClass); + if (fromRegistry != null) { + return fromRegistry; + } + final GenClass gc = genClass(eClass); + if (gc != null) { + return gc.getQualifiedInterfaceName(); + } // Unresolvable EClass (typically a proxy seen during editor reconciliation before linking // completes). Fall back to EObject so the inferred Java method signature is well-formed and the // reconciler can keep going; the next reconcile cycle will retry with the resolved type. - return EObject.name + return EObject.class.getName(); } /** @@ -174,79 +209,145 @@ def String qualifiedSwitchClassName(EPackage it) { * the Java name without needing the workspace GenModel to be indexed - which the inferrer cannot rely on * during editor reconciliation, since the {@code .genmodel} index population lags behind the open file. * - * @param it + * @param classifier * the classifier whose Java name is needed, may be {@code null} * @return the Java instance class name, or {@code null} if the EPackage is not in the registry or the * registry counterpart cannot be matched */ - def private String instanceClassNameFromRegistry(EClassifier it) { - val ePackage = EPackage - if (ePackage === null) { - return null + private String instanceClassNameFromRegistry(final EClassifier classifier) { + final EPackage ePackage = classifier.getEPackage(); + if (ePackage == null) { + return null; } - val nsURI = ePackage.nsURI - if (nsURI === null) { - return null + final String nsURI = ePackage.getNsURI(); + if (nsURI == null) { + return null; } - val registered = Registry.INSTANCE.getEPackage(nsURI) - if (registered === null || registered === ePackage) { + final EPackage registered = EPackage.Registry.INSTANCE.getEPackage(nsURI); + if (registered == null || registered == ePackage) { // Either the package is not in the registry, or what we already have IS the registry copy (and we // already checked its instanceClassName field above). - return null + return null; } - val counterpart = registered.getEClassifier(name) - if (counterpart === null) { - return null + final EClassifier counterpart = registered.getEClassifier(classifier.getName()); + if (counterpart == null) { + return null; } - counterpart.instanceClassName + return counterpart.getInstanceClassName(); } - def /*cached*/ GenPackage genPackage(EModelElement it) { - val ePackage = EcoreUtil2.getContainerOfType(it, EPackage) - val ctx = context.get() - if (globalScopeProvider !== null && ctx !== null) { - val scope = globalScopeProvider.getScope(ctx, GenModelPackage.Literals.GEN_MODEL__GEN_PACKAGES, null) - if (scope !== null && ePackage !== null) { - val desc = scope.getSingleElement(QualifiedName.create(ePackage.nsURI)) - if (desc !== null) { - return EcoreUtil.resolve(desc.EObjectOrProxy, ctx) as GenPackage + public GenPackage genPackage(final EModelElement element) { + final EPackage ePackage = EcoreUtil2.getContainerOfType(element, EPackage.class); + final Resource ctx = context.get(); + if (globalScopeProvider != null && ctx != null) { + final IScope scope = globalScopeProvider.getScope(ctx, GenModelPackage.Literals.GEN_MODEL__GEN_PACKAGES, null); + if (scope != null && ePackage != null) { + final IEObjectDescription desc = scope.getSingleElement(QualifiedName.create(ePackage.getNsURI())); + if (desc != null) { + return (GenPackage) EcoreUtil.resolve(desc.getEObjectOrProxy(), ctx); } else { - val resourceDescriptions = resourceDescriptionsProvider.getResourceDescriptions(ctx) - val descs = resourceDescriptions.getExportedObjects(GenModelPackage.Literals.GEN_PACKAGE, QualifiedName.create(ePackage.nsURI), false).iterator - if (descs.hasNext) { - return EcoreUtil.resolve(descs.next.EObjectOrProxy, ctx) as GenPackage + final IResourceDescriptions resourceDescriptions = resourceDescriptionsProvider.getResourceDescriptions(ctx); + final Iterator descs = resourceDescriptions + .getExportedObjects(GenModelPackage.Literals.GEN_PACKAGE, QualifiedName.create(ePackage.getNsURI()), false).iterator(); + if (descs.hasNext()) { + return (GenPackage) EcoreUtil.resolve(descs.next().getEObjectOrProxy(), ctx); } // In case Xcore is installed GenPackages will be indexed using GenPackage#getQualifiedPackageName() - for (candidate : resourceDescriptions.getExportedObjectsByType(GenModelPackage.Literals.GEN_PACKAGE).filter[name.lastSegment == ePackage.name]) { - val resolvedCanidate = EcoreUtil.resolve(candidate.EObjectOrProxy, ctx) as GenPackage - if (!resolvedCanidate.eIsProxy && resolvedCanidate.getEcorePackage == ePackage) { - return resolvedCanidate + for (final IEObjectDescription candidate : resourceDescriptions.getExportedObjectsByType(GenModelPackage.Literals.GEN_PACKAGE)) { + if (!Objects.equals(candidate.getName().getLastSegment(), ePackage.getName())) { + continue; + } + final GenPackage resolvedCanidate = (GenPackage) EcoreUtil.resolve(candidate.getEObjectOrProxy(), ctx); + if (!resolvedCanidate.eIsProxy() && Objects.equals(resolvedCanidate.getEcorePackage(), ePackage)) { + return resolvedCanidate; } } } } } - val resourceSet = if (ctx !== null) ctx.resourceSet else if (it.eResource.resourceSet !== null) it.eResource.resourceSet else new ResourceSetImpl - return if (ePackage !== null) GenModelUtil2.findGenPackage(ePackage, resourceSet) else null + final ResourceSet resourceSet; + if (ctx != null) { + resourceSet = ctx.getResourceSet(); + } else if (element.eResource().getResourceSet() != null) { + resourceSet = element.eResource().getResourceSet(); + } else { + resourceSet = new ResourceSetImpl(); + } + return ePackage != null ? GenModelUtil2.findGenPackage(ePackage, resourceSet) : null; + } + + public GenModel genModel(final EModelElement element) { + final GenPackage genPackage = genPackage(element); + return genPackage != null ? genPackage.getGenModel() : null; } - def /*cached*/ GenModel genModel(EModelElement it) { - val genPackage = genPackage(it) - return if (genPackage !== null) genPackage.genModel else null + public GenClass genClass(final EClass eClass) { + final GenPackage genPackage = genPackage(eClass); + return genPackage != null ? (GenClass) genPackage.getGenModel().findGenClassifier(eClass) : null; } - def /*cached*/ GenClass genClass(EClass it) { - val genPackage = genPackage(it) - return if (genPackage !== null) genPackage.genModel.findGenClassifier(it) as GenClass else null + public GenDataType genDataType(final EDataType dataType) { + final GenPackage genPackage = genPackage(dataType); + return genPackage != null ? (GenDataType) genPackage.getGenModel().findGenClassifier(dataType) : null; } - def /*cached*/ GenDataType genDataType(EDataType it) { - val genPackage = genPackage(it) - return if (genPackage !== null) genPackage.genModel.findGenClassifier(it) as GenDataType else null; + public String format(final String name) { + return GenModelUtil2.format(name); } - def /*cached*/ String format(String name) { - GenModelUtil2.format(name) + public String literalIdentifier(final ENamedElement element) { + return switch (element) { + case EClass eClass -> _literalIdentifier(eClass); + case EStructuralFeature feature -> _literalIdentifier(feature); + case null -> _literalIdentifier((Void) null); + default -> _literalIdentifier(element); + }; + } + + public String instanceClassName(final EClassifier classifier) { + return switch (classifier) { + case EClass eClass -> _instanceClassName(eClass); + case EDataType dataType -> _instanceClassName(dataType); + case null -> _instanceClassName((Void) null); + default -> _instanceClassName(classifier); + }; + } + + /** + * Returns the first interface directly implemented by the given class, or {@code null} if it implements none. + *

+ * Reproduces the {@code IterableExtensions.head} semantics the Xtend source relied on, where an empty + * sequence yields {@code null} rather than failing. + *

+ * + * @param type + * the class whose first declared interface is wanted, must not be {@code null} + * @return the first declared interface, or {@code null} if there is none + */ + private static Class firstInterface(final Class type) { + final Class[] interfaces = type.getInterfaces(); + return interfaces.length == 0 ? null : interfaces[0]; + } + + /** + * Returns the given string with its first character converted to upper case. + *

+ * Reproduces the {@code StringExtensions.toFirstUpper} semantics the Xtend source relied on, including its + * pass-through of {@code null}, of the empty string and of strings that already start upper case. + *

+ * + * @param value + * the string to convert, may be {@code null} + * @return the converted string, {@code null} if the input was {@code null} + */ + private static String toFirstUpper(final String value) { + if (value == null || value.isEmpty() || Character.isUpperCase(value.charAt(0))) { + return value; + } + if (value.length() == 1) { + return value.toUpperCase(); + } + return value.substring(0, 1).toUpperCase() + value.substring(1); } } diff --git a/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GeneratorUtilX.java b/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GeneratorUtilX.java index 1bf73f875f..a5773a0aae 100644 --- a/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GeneratorUtilX.java +++ b/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/GeneratorUtilX.java @@ -1,30 +1,45 @@ -package com.avaloq.tools.ddk.xtext.expression.generator +/******************************************************************************* + * Copyright (c) 2016 Avaloq Group AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Avaloq Group AG - initial API and implementation + *******************************************************************************/ +package com.avaloq.tools.ddk.xtext.expression.generator; -import java.util.Set -import org.eclipse.emf.ecore.EClass -import org.eclipse.emf.ecore.EObject -import org.eclipse.xtext.Grammar -import com.avaloq.tools.ddk.xtext.util.EObjectUtil +import java.util.Set; -class GeneratorUtilX { +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.Grammar; - def String xmlContributorComment(String source) { - '' +import com.avaloq.tools.ddk.xtext.util.EObjectUtil; + + +@SuppressWarnings("nls") +public class GeneratorUtilX { + + public String xmlContributorComment(final String source) { + return ""; } - def String javaContributorComment(String source) { - '// contributed by ' + source + public String javaContributorComment(final String source) { + return "// contributed by " + source; } - def String location(EObject obj) { - EObjectUtil.getFileLocation(obj) + public String location(final EObject obj) { + return EObjectUtil.getFileLocation(obj); } - def Set allInstantiatedTypes(Grammar it) { - GeneratorUtil.allInstantiatedTypes(it) + public Set allInstantiatedTypes(final Grammar grammar) { + return GeneratorUtil.allInstantiatedTypes(grammar); } - def boolean canContain(EClass it, Set others, Grammar g) { - GeneratorUtil.canContain(it, others, g) + public boolean canContain(final EClass eClass, final Set others, final Grammar g) { + return GeneratorUtil.canContain(eClass, others, g); } -} \ No newline at end of file + +} diff --git a/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/Naming.java b/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/Naming.java index 6e02a00cdd..7644437ca4 100644 --- a/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/Naming.java +++ b/com.avaloq.tools.ddk.xtext.expression/src/com/avaloq/tools/ddk/xtext/expression/generator/Naming.java @@ -8,23 +8,26 @@ * Contributors: * Avaloq Group AG - initial API and implementation *******************************************************************************/ +package com.avaloq.tools.ddk.xtext.expression.generator; -package com.avaloq.tools.ddk.xtext.expression.generator +import org.eclipse.xtext.util.Strings; -import org.eclipse.xtext.util.Strings -class Naming { +@SuppressWarnings("nls") +public class Naming { - def toFileName(String qualifiedName) { - qualifiedName.toJavaPackage.replace('.', '/') + '/' + qualifiedName.toSimpleName + ".java" + private static final String DOT = "."; + + public String toFileName(final String qualifiedName) { + return toJavaPackage(qualifiedName).replace(DOT, "/") + "/" + toSimpleName(qualifiedName) + ".java"; } - def toJavaPackage(String qualifiedName) { - Strings.skipLastToken(qualifiedName, '.') + public String toJavaPackage(final String qualifiedName) { + return Strings.skipLastToken(qualifiedName, DOT); } - def toSimpleName(String qualifiedName) { - Strings.lastToken(qualifiedName, '.') + public String toSimpleName(final String qualifiedName) { + return Strings.lastToken(qualifiedName, DOT); } } From 9c2e4e940e9fa7a221c3d6f817c23055e92f01c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dinis=20Ferreira?= Date: Wed, 2 Sep 2026 22:08:52 +0200 Subject: [PATCH 3/3] build: bump xtext.expression to 17.3.3 and runtime.feature to 19.1.1 (content changed vs baseline) The compare-version-with-baselines gate rejects the migrated bundle and the runtime feature that includes it: their content differs from the 19.1.0 baseline while the versions were unchanged. Micro bumps per OSGi conventions; the public API is identical. com.avaloq.tools.ddk.feature is already at 19.1.1 on master. Co-Authored-By: Claude Fable 5.1 --- com.avaloq.tools.ddk.runtime.feature/feature.xml | 2 +- com.avaloq.tools.ddk.runtime.feature/pom.xml | 2 +- com.avaloq.tools.ddk.xtext.expression/META-INF/MANIFEST.MF | 2 +- com.avaloq.tools.ddk.xtext.expression/pom.xml | 2 +- ddk-repository/category.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/com.avaloq.tools.ddk.runtime.feature/feature.xml b/com.avaloq.tools.ddk.runtime.feature/feature.xml index 92396d6693..753f53002a 100644 --- a/com.avaloq.tools.ddk.runtime.feature/feature.xml +++ b/com.avaloq.tools.ddk.runtime.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/com.avaloq.tools.ddk.runtime.feature/pom.xml b/com.avaloq.tools.ddk.runtime.feature/pom.xml index 6caa847071..e4576b75b9 100644 --- a/com.avaloq.tools.ddk.runtime.feature/pom.xml +++ b/com.avaloq.tools.ddk.runtime.feature/pom.xml @@ -6,7 +6,7 @@ 18.0.1-SNAPSHOT ../ddk-parent - 19.1.0-SNAPSHOT + 19.1.1-SNAPSHOT com.avaloq.tools.ddk.runtime.feature eclipse-feature diff --git a/com.avaloq.tools.ddk.xtext.expression/META-INF/MANIFEST.MF b/com.avaloq.tools.ddk.xtext.expression/META-INF/MANIFEST.MF index 366e4f8545..a63a30d469 100644 --- a/com.avaloq.tools.ddk.xtext.expression/META-INF/MANIFEST.MF +++ b/com.avaloq.tools.ddk.xtext.expression/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: com.avaloq.tools.ddk.xtext.expression Bundle-SymbolicName: com.avaloq.tools.ddk.xtext.expression;singleton:=true -Bundle-Version: 17.3.2.qualifier +Bundle-Version: 17.3.3.qualifier Bundle-Vendor: Avaloq Group AG Bundle-RequiredExecutionEnvironment: JavaSE-21 Require-Bundle: org.eclipse.xtext, diff --git a/com.avaloq.tools.ddk.xtext.expression/pom.xml b/com.avaloq.tools.ddk.xtext.expression/pom.xml index 2050902799..f349a3b008 100644 --- a/com.avaloq.tools.ddk.xtext.expression/pom.xml +++ b/com.avaloq.tools.ddk.xtext.expression/pom.xml @@ -6,7 +6,7 @@ 18.0.1-SNAPSHOT ../ddk-parent - 17.3.2-SNAPSHOT + 17.3.3-SNAPSHOT com.avaloq.tools.ddk com.avaloq.tools.ddk.xtext.expression eclipse-plugin diff --git a/ddk-repository/category.xml b/ddk-repository/category.xml index 5a1067f01f..531e277120 100644 --- a/ddk-repository/category.xml +++ b/ddk-repository/category.xml @@ -3,7 +3,7 @@ - +