In many cases, you will want your DSLs concepts to be usable as Java elements. E.g. an Entity will become a Java class and should be usable as such. In the domain model example, you can write
entity Employee extends Person {
boss: Person
...
entity Person {
friends: List<Person>
...
i.e. use entities instead of Java types or even mix Java types as List with entities such as Person. One way to achieve this is to let your concepts inherit from a corresponding JVM type, e.g. let Entity inherit from JvmGenericType (src). But this would result in a lot of accidentally inherited properties in your domain model. In Xbase there is an alternative: You can define how to derive a JVM model from your model. This inferred JVM model is the representation of your concepts in the typesystem of Xbase.
The main component for the inferred JVM model is the IJvmModelInferrer (src). It has a single method that gets a model element passed in and returns a list of JvmGenericTypes (src). As Xbase cannot guess how you would like to map your concepts to JVM elements, you have to implement this component yourself. This usually boils down to use an injected TypesFactory (src) to create a hierarchy of JVM elements, initialize that with values from the input model, and eventually use an injected IJvmModelAssociator (src) to associate the model elements with the JVM elements. As this kind of transformation can be elegantly implemented using polymorphic dispatch functions and extension methods, it is a good choice to write the IJvmModelInferrer (src) in Xtend.
For our domain model example, we call a recursive polymorphic dispatch function transform to traverse the containment hierarchy of the source model and generate JVM elements on the way. We transform each Entity to a JvmGenericType (src) that holds a JvmOperation (src) for each Operation and a JvmField (src) plus access methods for each Property. Whenever we have to copy a subtree, e.g. for setting the supertype of the JvmGenericType (src), we use EcoreUtil2 (src) .cloneWithProxies() to avoid eager resolution of referenced types. The resulting DomainmodelJvmModelInferrer looks like this:
class DomainmodelJvmModelInferrer implements IJvmModelInferrer {
@Inject TypesFactory typesFactory
@Inject extension IJvmModelAssociator jvmModelAssociator
override List<JvmDeclaredType> inferJvmModel(EObject sourceObject) {
sourceObject.disassociate
transform( sourceObject ).toList
}
def dispatch Iterable<JvmDeclaredType> transform(
Domainmodel model) {
model.elements.map(e | transform(e)).flatten
}
def dispatch Iterable<JvmDeclaredType> transform(
PackageDeclaration packageDecl) {
packageDecl.elements.map(e | transform(e)).flatten
}
def dispatch Iterable<JvmDeclaredType> transform(Entity entity) {
val jvmClass = typesFactory.createJvmGenericType
jvmClass.simpleName = entity.name
jvmClass.packageName =
(entity.eContainer as PackageDeclaration).name
entity.associatePrimary(jvmClass)
jvmClass.visibility = JvmVisibility::PUBLIC
if (entity.superType != null)
jvmClass.superTypes += cloneWithProxies(entity.superType)
for(f : entity.features) {
transform(f, jvmClass)
}
newArrayList(jvmClass as JvmDeclaredType)
}
def dispatch Iterable<JvmDeclaredType> transform(Import i) {
emptyList
}
def void transform(Feature feature, JvmGenericType type) {
val jvmField = typesFactory.createJvmField
jvmField.simpleName = feature.name
jvmField.type = cloneWithProxies(feature.type)
jvmField.visibility = JvmVisibility::PRIVATE
type.members += jvmField
feature.associatePrimary(jvmField)
val jvmGetter = typesFactory.createJvmOperation
jvmGetter.simpleName = "get" + feature.name.toFirstUpper
jvmGetter.returnType = cloneWithProxies(feature.type)
jvmGetter.visibility = JvmVisibility::PUBLIC
type.members += jvmGetter
feature.associatePrimary(jvmGetter)
val jvmSetter = typesFactory.createJvmOperation
jvmSetter.simpleName = "set" + feature.name.toFirstUpper
val parameter = typesFactory.createJvmFormalParameter
parameter.name = feature.name.toFirstUpper
parameter.parameterType = cloneWithProxies(feature.type)
jvmSetter.visibility = JvmVisibility::PUBLIC
jvmSetter.parameters += parameter
type.members += jvmSetter
feature.associatePrimary(jvmSetter)
}
}
As Java elements and your concepts are now represented as JVM model elements, other models can now transparently link to Java or your DSL. In other words, you can use a mapped element of your DSL in the same places as the corresponding Java type.
The Xbase framework will automatically switch between the JVM element or the DSL element when needed, e.g. when following hyperlinks. The component allowing to navigate between the source model and the JVM model is called IJvmModelAssociations (src), the read-only antagonist of the IJvmModelAssociator (src).
By default, the inferred model is indexed, so it can be cross referenced from other models. In Xtext 2.0.0 you still have to make sure the qualified names are computed correctly by implementing and binding an appropriate IQualifiedNameProvider (src):
public class DomainmodelQualifiedNameProvider
extends DefaultDeclarativeQualifiedNameProvider {
@Inject
private IQualifiedNameConverter converter;
QualifiedName qualifiedName(JvmGenericType type) {
return converter.toQualifiedName(type.getQualifiedName());
}
}
Several MWE generator fragments have additional properties for the JVM model inference:
| Fragment | Property | Purpose | Default |
| XbaseGeneratorFragment (src) | useInferredJvmModel | Generate a IJvmModelInferrer (src) stub and hooks for an inferred JVM model | true |
| XbaseGeneratorFragment (src) | generateXtendInferrer | Generate the IJvmModelInferrer (src) stub in Xtend instead of Java | true |
| RefactorElementNameFragment (src) | useJdtRefactoring | Always trigger JDT refactoring and register element rename refactoring as a participant thereof | false |