![]() |
Using local name with Merl |
Post Reply
|
| Author | |
MerlUser
Member
Joined: 13.Jul.2010 Location: Germany Points: 3 |
Post Options
Thanks(0)
Quote Reply
Topic: Using local name with MerlPosted: 13.Jul.2010 at 18:17 |
|
How can i access the "local name" of a property? With the following code, I was able to make a list of properties with the type-name and its value, but in my case there are more than one property of the same type (but with different local names):
foreach .() {
'<' type%var do :() { ' ' type%var '="' id%xml '"' } ' />' newline } Here is an example output:
<state typeA="a" typeB="b" typeA="c" />
In XML-files it is not allowed to define different attributes with the same name. The local names of the properties in the metaedit object have already different names. So i want to use these local names of the metaedit object properties, but how can I use these local names from merl?
|
|
![]() |
|
stevek
MetaCase
Joined: 11.Mar.2008 Points: 643 |
Post Options
Thanks(0)
Quote Reply
Posted: 16.Jul.2010 at 02:20 |
|
It looks like you're trying to write a generic generator, i.e. one that can be used for any metamodel. They are actually rather hard, because you need reflection capabilities to read the metamodel information. A normal generator only needs to read the model information, and the generator itself contains direct references to particular types, local names etc.
If you really want your model transformed in a watertight way into XML, I'd suggest you look instead at MetaEdit+'s XML export. That already takes care of the many XML challenges such as object references, circularity, illegal characters etc. that you will face after overcoming this hurdle. It should be easy enough to massage the XML export format into the format you want, e.g. with XSLT. On the other hand, if this was just a quick example to play around with MERL, I'd suggest you look at a different task. For a generator to add value, it has to understand the different semantics of each piece of information in the models, rather than just treating them all the same. For instance, normally one property will go to make up the name of a file, another to become a function name, still another will become part of a condition. Each property will thus be named explicitly in the generator, rather than iterating over all of them with "do :()". If you really want the local names, MERL doesn't have direct access to them [Edit 2018: now it does!]: it's designed to operate on models, not metamodels. You can however get them by saving the metamodel into text with: internal 'document:into: "' type '" metamodels' execute and reading that into a variable with: variable 'metamodel' write filename 'metamodels' sep type sep 'metamodel.txt' read close You can then iterate over the lines of that variable with "do $metamodel" - you'll have to do a little processing, but you can look at the "Reverse engineer Java" generator in UML's Class Diagram for hints. Of course, if you're only going to do this for one metamodel, there's no need to write a metaprogram that could do it for all metamodels. Just stick direct references to the local names into the generator: it's almost certainly faster in the long run, and also gives you a measure of indirection to cope with name changes in the future (e.g. if you want to keep the same XML schema but make the user-visible names in the modeling tool more user-friendly). do .State { '<state name="' :Name%xml '" description="' :Description%xml '/>' newline } Note how that is already simpler than what you have above, and easier to read. I'm a big fan of meta solutions, but even I have to admit that in some cases a simple solution, even one with some apparent duplication, may be best! Edited by stevek - 29.May.2018 at 09:29 |
|
![]() |
|
MerlUser
Member
Joined: 13.Jul.2010 Location: Germany Points: 3 |
Post Options
Thanks(0)
Quote Reply
Posted: 19.Jul.2010 at 13:26 |
|
Thanks a lot for your detailed reply.
Yes, I would like to write a generic generator, because my meta-model contains a specific relation on which several different objects are connected. It would be nice to iterate over these objects and its properties in a generic way to write all the data to a XML file. It would be very nice to have access also to the local name. The interpreter of merl is already able to iterate over all properties and gives access to "type" and "value". I am thinking of a key word like "localname" for having access to the local name of a property. In my opinion it would be easy for you to add this to the merl-language, isn't it? |
|
![]() |
|
stevek
MetaCase
Joined: 11.Mar.2008 Points: 643 |
Post Options
Thanks(0)
Quote Reply
Posted: 22.Jul.2010 at 14:52 |
|
It actually wouldn't be trivial to add local name: inside do :(), the elements we are iterating over are properties. A property doesn't know its local name, because the same property can be shared between several objects, maybe of different types or in different slots, each with its own local name.
While we can work around that, adding "localName" opens up a large number of other new commands that would then be needed, e.g. to find the datatype of the property type to know whether to recurse again with do :() over a contained object or collection; to find the supertype of the current type; to find the kind of List widget a property type uses and its legal values etc. Essentially, MERL is a language for navigating models, and doesn't have commands to navigate metamodels. Adding those commands would increase the size of MERL by at least 50% - and the topic next to yours in the forum shows that some people already need to take a big breath before getting started.
Of course, we recognise the desire to write meta-programs in MERL, and indeed we use that freely in some areas (e.g. subreport '_' type run). While there is a little duplication involved in writing local names explicitly in generators, as well as in the metamodel, that's only for one person - the metamodeler - and not for all the modelers. Unless you have hundreds of types with dozens of properties each, that shouldn't be too bad.
BTW Maybe one solution for you, if all you need is uniqueness of attribute names, would be to add a number to the end of the name:
$ix='0'
do :() { ... type%var $++ix ...}
|
|
![]() |
|
MerlUser
Member
Joined: 13.Jul.2010 Location: Germany Points: 3 |
Post Options
Thanks(0)
Quote Reply
Posted: 26.Jul.2010 at 14:59 |
|
The last example you give is not the best solution, but it works for my problem.
Now I changed the most type names of the properties to the local name. Only in cases where are more than one properties with the same type, I add extra code like this: do :() { if type<>'exceptionType' then ' ' type%var '="' id%xml '"' endif } ' exceptionOne="' :exceptionOne%xml '"' ' exceptionTwo="' :exceptionTwo%xml '"' |
|
![]() |
|
stevek
MetaCase
Joined: 11.Mar.2008 Points: 643 |
Post Options
Thanks(0)
Quote Reply
Posted: 26.Jul.2010 at 15:15 |
|
Great! That's a nice combination of a meta-solution with pragmatism.
Pragmatism is definitely needed when working with XML. It looks simple, but actually the full rules are a nightmare, so trying to write code on day 1 that fulfills all the rules is a lost cause. E.g. how many remember that the name of an attribute cannot start with the letters "xml" in any combination of cases, but it can start with a colon?
|
|
![]() |
|
djuka
Major Contributor
Joined: 22.Jun.2011 Location: Germany Points: 33 |
Post Options
Thanks(0)
Quote Reply
Posted: 16.Feb.2012 at 10:35 |
|
Hello !
"In protocols" is property defined as collection. I could not find how to iterate over collection members. For me is not problem to get metadata, this can be fixed, but iterating over elements is important. If I use id complete collection is printed. Any idea how to solve this problem in MERL (and MetaEdit+ beta 5.0). MERL: if type ='In protocols' then ' void On' id '()' newline endif Output: void OnIn:HasContact[Boolean] In:FullPress[Boolean] In:CurrPos[double] Regards, Djuka |
|
![]() |
|
janne
MetaCase
Joined: 25.Mar.2008 Points: 58 |
Post Options
Thanks(0)
Quote Reply
Posted: 16.Feb.2012 at 11:08 |
|
Hi,
How about iterating the collection with: ' void On' do :In protocols; { id ' '} newline Or (if you don't want to have extra space after the last reported item) ' void On' dowhile :In protocols; { id ' '} newline While in your sample you have metatype testing for the property field, I assume you are iterating over all properties (have do :() {...}). If that is the case, then you need to index the inner do loop with 1, following sample should work: do :() { if type ='In protocols' then ' void On' dowhile :In protocols;1 {id ' ' } newline endif } More samples and full MERL documentation can be found from: http://www.metacase.com/support/45/manuals/mwb/Mw.html Edited by janne - 16.Feb.2012 at 11:10 |
|
![]() |
|
djuka
Major Contributor
Joined: 22.Jun.2011 Location: Germany Points: 33 |
Post Options
Thanks(0)
Quote Reply
Posted: 16.Feb.2012 at 11:56 |
|
Yes, that's it !
Thanks a lot ! This generates C++ classes: MERL: Report 'Controlers' foreach .Actor; { if :IsControler; = 'T' then '#ifndef C_' :Name;'_HEADER_H_' newline '#define C_' :Name;'_HEADER_H_' newline newline 'Class C' :Name; newline '{ public:' newline ' C' :Name; '(TSensorValue min,TSensorValue max,TSensorValue curr) : CGenericSensor(min,max,curr) { //TODO: ??? }' newline ' virtual~C' :Name; '(void) { }' newline ' void On' :Name; 'Press() { sensorDown(); };' newline ' void On' :Name; 'Release() { sensorUp(); };' newline '// Declaration of event handlers' newline do :(); { if type ='In protocols' then do :In protocols;1 { ' void On' :Signal name; '();' newline } endif } newline '// Declaration of members' newline do :(); { if type ='In protocols' then do :In protocols;1 { :Data type; ' m_' :Signal name; ' = ' :Default value;';' newline } endif } newline '#endif' newline newline endif } endreport Output: #ifndef C_TempoLimitController_HEADER_H_ #define C_TempoLimitController_HEADER_H_ Class CTempoLimitController { public: CTempoLimitController(TSensorValue min,TSensorValue max,TSensorValue curr) : CGenericSensor(min,max,curr) { //TODO: ??? } virtual~CTempoLimitController(void) { } void OnTempoLimitControllerPress() { sensorDown(); }; void OnTempoLimitControllerRelease() { sensorUp(); }; // Declaration of event handlers void OnOnOff(); void OnSpeedCurr(); // Declaration of members Boolean m_OnOff = false; int m_SpeedCurr = 0; #endif |
|
![]() |
|
Post Reply
|
|
| Tweet |
| Forum Jump | Forum Permissions ![]() You cannot post new topics in this forum You cannot reply to topics in this forum You cannot delete your posts in this forum You cannot edit your posts in this forum You cannot create polls in this forum You cannot vote in polls in this forum |