Use !include from within a Macro?

0 votes
asked Jul 17 in Question / help by StefanDensow (250 points)
retagged Jul 18 by StefanDensow

Hi, I'm using !include heavily and would like to wrap it inside a macro. In essence, I need the following macro to work:

!define MY_INCLUDE(FILE, ID) !include FILE!ID

which I call like this:

MY_INCLUDE(MyClass.iuml, MyID)

(file and MyID exist, of course). However, I get a syntax error complaining about: !include MyClass.iuml!MyID

Apparently the macros are replaced but the resulting text is not preprocessed again. Can you tell me how to get this to work?

Or alternatively, how I can write a macro that leads to the inclusion of a specific file/id along with some defined-by-me text before and after the !include.

Thanks a lot,

Stefan Densow

2 Answers

+1 vote
answered Sep 6 by plantuml (179,660 points)
selected Sep 7 by StefanDensow
 
Best answer

We have changed the implementation of the preprocessor in last beta http://beta.plantuml.net/plantuml.jar

Now, you can have:

@startuml
!define INCLUDE(FILE, ID) !includesub FILE!ID
INCLUDE(CMyClass.iuml, FuncB)
@enduml

Tell us if it helps!

commented Sep 7 by StefanDensow (250 points)
Awesome!! I was so thrilled that I didn't read your code sample fully but directly tried some define, that I want to apply, namely:

!definelong CALL_METHOD(FROM_CLASS, TO_CLASS, FUNC_NAME, ARG = "", RETURN_LABEL = "")
FROM_CLASS -> TO_CLASS: FUNC_NAME(ARG)
activate TO_CLASS
    !include_many TO_CLASS.iuml!FUNC_NAME
FROM_CLASS <-- TO_CLASS
deactivate TO_CLASS
!enddefinelong

And it works! =) Thanks a lot!

I'm currently working on a writeup that has some details and examples. I'll link it here once it's done.
commented Sep 7 by StefanDensow (250 points)
Hm, I tried swapping the plantuml beta into my preferred VSCode plugin ( https://marketplace.visualstudio.com/items?itemName=jebbs.plantuml ) but rendering with simple !include statements complains about missing include file (diagrams with !includes seem to work fine).
Now, I'm not expecting support for that plugin but I was wondering whether something has changed in the beta, that requires a different invocation to find include files correctly. If so, I'd like to pass that info on to the developer of said plugin.
commented Sep 7 by StefanDensow (250 points)
Hm, I'm playing with the beta and there may an issue, as if text replacement is done _within_ !define statements.
E.g. I have statements !define COLOR_BOX #cccccc in a file included in multiple places. Further down in the diagrams I see !define #cccccc #cccccc (in the metadata of an SVG file). This should not happen.
I observed that in one of my larger diagrams but couldn't boil it down to a minimal example, yet. I'll try to get back to that next week. Maybe that's helpful for you meanwhile.
commented Sep 7 by plantuml (179,660 points)
Several things have changed in the beta...
Have you some examples that are working using command line, and not with VSCode plugin ?
There is also a new:
@startuml
path
@enduml
That prints where PlantUML is reading files from !include.

Finally, you can send us by email (plantuml@gmail.com) some examples of non-working diagram so that we can have a look on it.
commented Sep 7 by plantuml (179,660 points)
Fixed the !define issue in last beta http://beta.plantuml.net/plantuml.jar

Even the following one was not working:
@startuml
!define COLOR_BOX #ccccc
!define COLOR_BOX #ccccc
alice -> bob : print COLOR_BOX
@enduml
There are probably some issues remaining when mixing !define and !definelong. This is still a beta :-)
commented Sep 7 by StefanDensow (250 points)
Very cool! Now all my old big diagrams work again =)
I then replaced all my !include statements with the CALL_METHOD define from above and it works everywhere, really cool!

I also had a look at the path feature but it didn't give me any insight why file inclusion fails. Some inspection revealed that the VSCode plugin sets '-Djava.awt.headless=true' and '-Duser.dir=<diagram-dir>'. Maybe the beta changed behavior in that regard..

Anyway, thanks a lot for your effort! :)
0 votes
answered Jul 26 by StefanDensow (250 points)

Hi again,

I had look at the source code and made some progress. I found out that - contrary to !include - !includesub is processed after !define are applied. Exactly what I need! That led to the following simple solution:

Rendered file:

@startuml
!define INCLUDE(FILE, ID) !includesub FILE!ID
INCLUDE(CMyClass.iuml, FuncB)
@enduml

Included file:

@startuml
!startsub FuncB
note over CMyClass: Hello from FuncB!
!endsub
@enduml

However, when trying to apply this to my actual problem, plantuml crashes (version 1.2018.08 and the beta from beta.plantuml.net uploaded 2018-07-25 15:07).

I'm trying to use the preprocessor to generate a file similar to this (image):

@startuml
SomeClass -> CMyClass: FuncA()
activate CMyClass
    note over CMyClass: Hello from FuncA!
    CMyClass -> CMyClass: FuncB()
    activate CMyClass
        note over CMyClass: Hello from FuncB!
    CMyClass <-- CMyClass
    deactivate CMyClass
SomeClass <-- CMyClass
deactivate CMyClass
@enduml

This is the rendered file, that I'm using:

@startuml
!definelong CALL_METHOD(_fromClass, _toClass, _func, _args = "", _return = "")
    _fromClass -> _toClass: _func()
    activate _toClass
        !includesub _toClass.iuml!_func
    _fromClass <-- _toClass: _return
    deactivate _toClass
!enddefinelong

' crashes, works when CALL_METHOD in FuncA is removed
CALL_METHOD(SomeClass, CMyClass, FuncB)

' crashes
'CALL_METHOD(SomeClass, CMyClass, FuncA)
@enduml

And this is the included file CMyClass.iuml:

@startuml

!startsub FuncA
note over CMyClass: Hello from FuncA!
CALL_METHOD(CMyClass, CMyClass, FuncB)
!endsub

!startsub FuncB
note over CMyClass: Hello from FuncB!
!endsub

@enduml

This is the error message I get:

$> java -classpath .\target\classes\ net.sourceforge.plantuml.Run -tpng .\test_include.puml
Exception in thread "main" java.lang.IllegalStateException
        at net.sourceforge.plantuml.preproc.Defines.saveState(Defines.java:181)
        at net.sourceforge.plantuml.preproc.Preprocessor.<init>(Preprocessor.java:79)
        at net.sourceforge.plantuml.preproc.SubPreprocessor.manageIncludeSub(SubPreprocessor.java:131)
        at net.sourceforge.plantuml.preproc.SubPreprocessor.readLine(SubPreprocessor.java:99)
        at net.sourceforge.plantuml.preproc.Preprocessor.readLine(Preprocessor.java:95)
        at net.sourceforge.plantuml.preproc.SubPreprocessor.manageIncludeSub(SubPreprocessor.java:133)
        at net.sourceforge.plantuml.preproc.SubPreprocessor.readLine(SubPreprocessor.java:99)
        at net.sourceforge.plantuml.preproc.Preprocessor.readLine(Preprocessor.java:95)
        at net.sourceforge.plantuml.BlockUmlBuilder.init(BlockUmlBuilder.java:86)
        at net.sourceforge.plantuml.BlockUmlBuilder.<init>(BlockUmlBuilder.java:68)
        at net.sourceforge.plantuml.SourceFileReader.<init>(SourceFileReader.java:86)
        at net.sourceforge.plantuml.Run.manageFileInternal(Run.java:432)
        at net.sourceforge.plantuml.Run.processArgs(Run.java:361)
        at net.sourceforge.plantuml.Run.manageAllFiles(Run.java:331)
        at net.sourceforge.plantuml.Run.main(Run.java:179)

Is there a way to fix this? Or even better, evaluate for !include after !define have been applied? Because this is the only reason I had to resort to !includesub instead of !include_many.

Any help is highly appreciated as I'm trying to apply plantuml to a large codebase and only this kind of preprocessor trickery would keep it managable.

BTW, plantuml is already really awesome. It's been extremely helpful to map existing architectures (I use class and sequence diagrams). Thanks a lot!

commented Jul 26 by plantuml (179,660 points)
Hello,

Thanks for the report.
We are currently working on this.
!include/!includesub/!include_many have not been implemented with "dynamic" filename in though, and this is cause of many issues.
We probably have to redesign this part of preprocessing, so it's going to take some times (September ?) Sorry about that.

As temporary workaround, you may take advantage of the new -preproc flag ( see http://forum.plantuml.net/7944/is-it-possible-to-run-only-the-preprocessor ) and work in two phases :
1) First you generate some .preproc files
2) Then you !include those .preproc files in your main files
 
I don't know if it's possible for you case?
...