I am using the DSL generator interface of Xtext in order to generate some models based on my Xtext DSL. This is working fine, but right now I am facing a bit of a problem in writing the generator. I am using the generator to filter out Rules declared in my Xtext DSL. I do this by selecting certain Rules and then convert them in an Iterable
object, which I can then use to filter on certain types (see the toIterable.filter()
parts in my code below). The code below contains one for loop which itself again contains 3 nested for loops. These nested loops all filter on one specific kind of Method Statement (types that I declared in my DSL). I would like to combine these 3 for loops in one for loop by passing the 3 types as parameters in the filter()
method. In this case there would be one nested for loop where the condition would ideally look something like:
for (eachMethodStatement : ifStatement.expression.eAllContents.toIterable.filter(StatementSort1, StatementSort2, StatementSort3)
The problem is that the filter()
method only takes one argument (one type), so right now I have to write three dispatch methods called getDemand
which all basically do the same. It works right now, but this forces me to write a lot of boilerplate code for each type that I want to filter.
Is there a way to filter multiple types (in one for loop) without creating a lot of boilerplate code?
for (ifStatement : ifElseStatement.eAllContents.toIterable.filter(IfStatements)){
for (persistenceFunction : ifStatement.expression.eAllContents.toIterable.filter(SingleLibraryPersistenceMethodStatement)) {
expressionDemand += getDemand(persistenceFunction,resourceTable)
}
for (interfaceFunction : ifStatement.expression.eAllContents.toIterable.filter(SingleLibraryInterFaceMethodStatement)) {
expressionDemand += getDemand(interfaceFunction,resourceTable)
}
for (businessFunction : ifStatement.expression.eAllContents.toIterable.filter(SingleLibraryBusinessMethodStatement)) {
expressionDemand += getDemand(businessFunction,resourceTable)
}
for (persistenceFunction : ifStatement.expression.eAllContents.toIterable.filter(RelationalOperator)) {
expressionDemand += getDemand(persistenceFunction,resourceTable)
}
}
<T> T filter(Class<T>)
cannot do multiple types because then the return type could not be T
but - in the extreme case Object
which must be cast later.
But: If your four types (SingleLibraryPersistenceMethodStatement
, SingleLibraryInterFaceMethodStatement
, SingleLibraryBusinessMethodStatement
, RelationalOperator
) share a specific interface or supertype you can used that with the existing filter()
method:
for (ifStatement : ifElseStatement.eAllContents.toIterable.filter(IfStatements)){
for (statement : ifStatement.expression.eAllContents.toIterable.filter(Statement)) {
expressionDemand += getDemand(statement,resourceTable)
}
}
def Demand getDemand(Statement statement, ResourceTable resourceTable){
...
}
Another solution is to avoid filter
in this case and use switch
with type guards like this:
for (ifStatement : ifElseStatement.eAllContents.toIterable.filter(IfStatements)){
for (it : ifStatement.expression.eAllContents) {
switch(it){
SingleLibraryPersistenceMethodStatement,
SingleLibraryPersistenceMethodStatement,
SingleLibraryBusinessMethodStatement:
expressionDemand += getDemand(it,resourceTable)
}
}
}