Search code examples
xtextiterablextend

Pass multiple types as parameter in filter function of Iterable object in Xtend


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)                            
        }
}

Solution

  • <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)
            }
        }
    }