How to substitute XXX.class.getName() in generation when class is returned by concept behavior?

I have a concept behavior method with java.lang.Class return type.

What is the way to generate  XXX.class.getName() when doing "to baseLanguage" generation?

If I try ReferenceMacro over Object:  ->$[ Object ] .class.getName() I get "type java.lang.Class is not subtype of node<Classifier>" error (reference target is implemented by mere "node.getOutputClass()")

...

After a bit of reflection I realized "concept behaviours should not be used to return java objects". Am I right?
So, instead of returning java objects like "java.util.Map" from concept behaviours and trying to convert it back to template form in generator, either relevant node<...> should be returned from behaviour or template switch should be used.

Is it the case?

PS. It looks like ClassifierResolveUtils.resolveSpecialSyntax could be used to convert from "class name as string to node<Classifier> form", however it looks to be much easier to avoid that and use node<Classifier> in behaviour return type.
12 comments
It is difficult to judge your case without knowing the motivation behind returning Class from the method. In general, it's advisable to stay on the model/meta-model level in behaviour methods that build parts of the generated code.
0
Some motivation is here: stackoverflow.com/questions/30383247/mps-way-of-attaching-additional-attributes-to-concepts-properties-references

I've attached my current code.
I'm trying to create a DSL for Apache JMeter (jmeter.apache.org/index.html).
I want sharing generator logic (just to avoid repeated code for each and every type of test element), so I created "AbstractTestElement_generator" that does the following:

1. Creates java object with specific class name, relevant for the concrete implementation of the concept.
2. Attaches current element to its parent tree
3. Applies COPY_SRC to children


The specific element calls $INCLUDE$AbstractTestElement_generator and does some concept-specific stuff.

So, my key problem is
P1) "how AbstractTestElement_generator would know the proper node < Classifier > to use when creating element"
P2) The one I listed at stackoverflow: "how could I attach metadata to concept properties, so I can move generic setProperty loop right into AbstractTestElement_generator".

Concepts
AbstractTestElement_concept.png (65KB)
Controller_concept.png (62KB)
ThreadGroup_concept.png (74KB)

Behaviors
ThreadGroup_generator.png (204KB)

Generators
AbstractTestElement_generator.png (209KB)
ThreadGroup_generator.png (204KB)
0
Do you have any idea how to attach images to the questions?
I've attached current code to both original question and the reply, however it does not appear in forum UI when browsing.
0
you have to use the wiki "[^...]" syntax (the "wiki help" link).
0
That works. Does the question make more sense now?
0
I guess what you're looking for is the node/X/ (e.g. node/String/) construct - this gives you a Classifier representing the class so that you get point to it from the reference macros.
0
Hello!

First or all, I propose to NOT use behaviour methods for parameterising generation process here.

I think, it's better to use template fragments for specifying exact JMeter (runtime) class representing specific concept (sub-concept of AbstractTestElement). If you'd like to keep single template for AbstractTestElement & instantiate specific classes for each sub-concept of AbstractTestElement concept, you can:
  • attach $SWITCH$ generator macros to both AbstractTestElement (ClassifierType) & TestPlan() (ClassCreator)
  • create TemplateSwitchs called from both macros
  • inside those switches add cases for each exact sub-concept of AbstractTestElement & use InlineTemplates to return proper ClassifierType/ClassCreator instances

See StyleAttribute template switch doing something similar in MPS.

Similar approach can be used to instantiate proper runtime class for properties: (String/LongProperty), but instead of template switch this time you have to use reference macro with java switch inside. If you prefer to reuse corresponding code across different reference macros - you can extract it into a java class inside generator and call corresponding class from the reference macros.

In general, my suggestions would be:
  • create single template for each generetable sub-concept of AbstractTestElement
  • inside this template simply write code like:
    ThreadGroup element = new ThreadGroup();
    prettyPrint();
  • when use $CALL$ template macro to call "common" template injecting common properties for all AbstractTestElements
  • when simply write all specific properties for this sub-concept
  • for each model property code like:
    tg.setProperty( new StringProperty(AbstractThreadGroup.NUM_THREADS, $SOPY_SRC$...));
    prettyPrint();
just like you have in the template now.

In MPS you can find similar "properties" inside editor language, see StyleClassItem concept. Our decision was to create exact sub-concept for each property "kind" and keep them all in the same plain list of properties (single containment role). The benefit here is: in this case you can create single template for each "property" generating complete code line like:
new StringProperty(AbstractThreadGroup.NUM_THREADS, $SOPY_SRC$...)
prettyPrint();
0
attach $SWITCH$ generator macros to both AbstractTestElement (ClassifierType) & TestPlan() (ClassCreator)


Thank you for the suggestion.

I wonder how that could be extended by "inherited languages".

As far as I can understand, a set of $SWITCH$ statements would be hard-coded in the language itself.
I wonder how that would be extendable.

When using behaviors, just adding more "AbstractTestElement subconcepts" makes thing work with core generator.

How a derived language would stuff it's own test element concepts into my $SWITCH$?


I do not quite get which mapping configuration would be used in presence of multiple derived languages.



inside this template simply write code like:
ThreadGroup element = new ThreadGroup();
prettyPrint();


I want to factor "new ThreadGroup" to the Abstract... generator since I need to associate a mapping label with that newly created item (see AbstractTestElement_generator.png above)
I just do not want copy&paste all those mapping code around, so I tried to factor that out.


just like you have in the template now.

That works, however, can you please shed some light on my P2 question?

Basically, if I could associate "AbstractThreadGroup.NUM_THREADS" with given concept property, I could move as much "boilerplate generation" as it is possible to the Abstract.. generator, so some of the components would be just "do Abstract.. generation".

Just a small little annotation over LinkDeclaration.
0
As for extending template switches, in your extending language you declare a switch that extends the switch from the extended language - this should do the trick.
0
In my "AbstractTestElement" generator I call my own instance of a switch template. I can't foresee all the extended switches in advance, can I?

How that "extended switch" would sneak into logic of my AbstractTestElement generator?
0
The generator will take all extending template switches into account when deciding on a SWITCH macro. https://confluence.jetbrains.com/display/MPSD32/Generator#Generator-TemplateSwitch is likely to shed some light into it.
0
Another option can be to pass
node<> n = <AbstractThreadGroup.NUM_THREADS>;
prettyPrint(); as a parameter into template generating
tg.setProperty(....)
prettyPrint(); code. You can create this node "in the air" and use as a parameter of template $CALL$ macro. I can't say that I prefer this way of using templates, by it will probably solve your problem. ;-)
0

Please sign in to leave a comment.