externalize entries and operations from <LPMX><OPER>

Author: francois.moritz@hewitt.com (fmoritz)


in the <LPMX> and <OPER> it's very difficult to manage / look at what's really in them even the ude editor tempt to show the developper the entry and operation lists

if entries / operations would be declared undependant of these triggers it'd be easier to manage them.

example: put some tags for every changes in a comment field for each entry / operation etc...

Why do not create an operation and entry table (uxoper and uxentry like uxregs...) for components ?

when we are fixing our application's  release, restauring previous version , so we could better manage the source code and when different developper are working onto the same component for different raisons.



  1. Hi Francois,

    for the moment I have 2 ways to get that info:

    1) Using textpad to read the *.pro with a routine to create a jumplist

    2) an AWK script which gives me a list of the entries including the parameter list



    The textpad jumplist code reads as


    'Script for creating a Textpad Reg exp output
    '- from *.UPL transfer files from UNIFACE-Assistant
    '- from *.PRO files
    '- from a UNIFACE TRX-file after TRX_CNV.COM from Warwick Sands
    '031125 U.Merkel
    'This is provided as is, use at your own risk!
    'Developed based on the FindVB.vbs provided by r_bondi@hotmail.com

    Option Explicit

    Dim inFile, outFile, Args, period
    Const COL_WIDTH = 42

    '---- Get input file, either from argument or prompt
    Set Args = WScript.Arguments 'Drag and Drop or Cmdline

    If Args.Count = 1 Then
        inFile = Args(0)
    End If

    Set Args = Nothing


    Sub Convert()
        Dim CurrentLine, skipCols
        Dim fs, f
        Dim pnt, slash, strt, tmpStr, CX, lineNo, comment, sidesOK
        Const ForReading = 1, ForWriting = 2, ForAppending = 8
        On Error Resume Next
        lineNo = 0

        '-- Some Error Checks --
        If inFile = "" Then
            Wscript.echo "No input file entered, Please re-run Script"
            Exit Sub
        End If
        '-- Get Input File --
        Set fs = CreateObject("Scripting.FileSystemObject")
        Set f = fs.OpenTextFile(inFile, ForReading)
        '-- Verify Input File --
        If f Is Nothing = true then
            Wscript.echo "Invalid Input File, Please re-run Script"
            Exit Sub
        End If

        '-- calculate how many columns have to be skipped
            skipCols = 1
            If (InStr(1,Right(inFile,3),"pro",1) ) Then
               skipCols = 0
            End If
            If (InStr(1,Right(inFile,3),"upl",1) ) Then
               skipCols = 0
            End If
        '-- Read all lines, one line at a time --
        Do While f.AtEndOfStream <> True
            CurrentLine = f.readline()
            If (skipCols > 0) Then
               CurrentLine = Mid(CurrentLine,skipCols)
            End If
            lineNo = lineNo + 1
            if (Left(CurrentLine,9) = "Trigger <") Then
                Wscript.echo CurrentLine &  " :" & infile & ":" & (lineNo) &  ":"
            End If

            rv = printIfFound(CurrentLine,LineNo,inFile,"#define")
            rv = printIfFound(CurrentLine,LineNo,inFile,"#for procid")
            rv = printIfFound(CurrentLine,LineNo,inFile,"#for funcid=")
            rv = printIfFound(CurrentLine,LineNo,inFile,"#for funcid")
            rv = printIfFound(CurrentLine,LineNo,inFile,"entry")
            rv = printIfFound(CurrentLine,LineNo,inFile,"operation")
            rv = printIfFound(CurrentLine,LineNo,inFile,"call")
            rv = printIfFound(CurrentLine,LineNo,inFile,"activate")
            rv = printIfFound(CurrentLine,LineNo,inFile,"run")
            rv = printIfFound(CurrentLine,LineNo,inFile,"repeat")
            rv = printIfFound(CurrentLine,LineNo,inFile,"until")
            rv = printIfFound(CurrentLine,LineNo,inFile,"while")
            rv = printIfFound(CurrentLine,LineNo,inFile,"endwhile")



        ' I think this happens automatically?
        Set f = Nothing
        Set fs = Nothing
    End Sub

    Function printIfFound(CurrentLine, LineNo, infile, Target)
       Dim fs, f
       Dim pnt, slash, strt, tmpStr, CX, comment, sidesOK

       strt = InStr(1, CurrentLine, Target,1)
       Comment = InStr(1, CurrentLine, ";")
       sidesOK = checkSides(strt, CurrentLine, Target, comment)
       If sidesOK Then
          pnt = Mid(CurrentLine, strt)
          pnt = Left(pnt, COL_WIDTH - 1)
          Wscript.echo pnt & Space(COL_WIDTH - len(pnt)) &  " :" & infile & ":" & (lineNo) &  ":"
       End If
       printIfFound = 0   
    End Function

    Function checkSides(strt, cl, str, comment)
    Dim tmpStr, chL, chR, rv
       rv = false
       chL = false
       chR = false
       If strt > 0 and ((strt < comment) or (comment < 1)) Then
          If Strt = 1 Then
     chL = true
     If Mid(cl, strt -1, 1) = " " or Mid(cl, strt -1, 1) = vbTab then chL = true
          End If
          If Len(cl) = strt + len(str) - 1 Then
     chR = true
     If Mid(cl, strt + len(str), 1) = " " or Mid(cl, strt + len(str), 1) = vbTab then chR = true
          End If
       End If
       if chL and chR then rv = true
       checkSides = rv
    End Function


    Author: ulrich-merkel (ulrichmerkel@web.de)
  2. thanks a lot ULrich
    but I probably did not complain clearly what I wanted to do.

    having every entries/operations in a single trigger (field in the repository) does not let us to create / adapt source codes at the same time from 2 differents developpers.
    Having entries / operations in a separate table (example UXENTRY / UXOPER) similar UXREGS would let us do it.
    So it could be imagine a comment / text could be implemented like in  Usource for global procs.
    In this matter of fact every comments put in such table would be associated as well. It not the case in <OPER><LPMX>

    example in a trigger
    ; Comment : of my_entry

    ; created by me on 01.01.2009
    ; modfied by him on 02.01.2009
    ; modified by me on 03.02.2009
    ; rules : ....

    entry My_Entry
       string p1 : in
       string p2:  out
    ; my comment ligne on 03.02.2009 for this example
    ;p2 = "Entry %%p1%%%"
    p2 = "MY entry %%p1%%%"

    ; Comment : of my_entry 2

    ; created by me on 01.02.2009
    ; rules : ....

    entry My_Entry2
       string p1 : in
       string p2:  out
    p2 = "MY entry2  %%p1%%%"

    > If you put comments into your code it's difficult to associate them to the entry.
    you 'd better have to include the comments into the entries like  :

    entry My_Entry
    ; created by me on 01.01.2009
    ; modfied by him on 02.01.2009
    ; modified by me on 03.02.2009
    ; rules : ....

       string p1 : in
       string p2:  out
    ; my comment ligne on 03.02.2009 for this example
    ;p2 = "Entry %%p1%%%"
    p2 = "MY entry %%p1%%%"

    entry My_Entry2
    ; Comment : of my_entry 2

    ; created by me on 01.02.2009
    ; rules : ....

       string p1 : in
       string p2:  out
    p2 = "MY entry2  %%p1%%%"

    I thing these contraintes are very ennoying and having a separate table to put entries / operations would let us take advantages from such an implementation of the repository.
    In the same way to :

    • find entries / operations etc...undepending on wherein the trigger there are declared.
    • adapt an entry / operation without having locked the component.
    • work with a central repository with get/put objects functions  when you have to update objects .
    • etc..










    Author: fmoritz (francois.moritz@hewitt.com)
  3. Hi Francois,

    I think I understood you very well:

    You want to have different code areas for different developers.

    That is the reason why I use *.pro as a base for scanning where it is totally unimportant which trigger holds the statemens
    or included procs are used;
    we are based on the output-listing of the compile process.

    To give each developer his own share of devspace,
    Just put a frame-marker in your component and use the different trigers there for the different developers.
    Or give each user a (hidden) field to play around, or ....
    (you can use "global updates" to scan the different sources)

    Another option would be to use included procs for each developer, so your <LPMX> is just a sequence of
    #include user_FM:<$componentname>
    #include user_UME:<$componentname>

    Success, Uli

    Author: ulrich-merkel (ulrichmerkel@web.de)
  4. I think that #include proc. can satisfy many of your needs.



    Author: addice (addice@yahoo.com)
  5. Except the ability to scan the contents of a component for particular functionality.
    Maintenance of a component with 40 or 50 include procs stacked in the operations/local procs trigger is an exercise in wearing out your mouse finger opening and closing different includes.

    Author: Iain Sharp (i.sharp@pcisystems.co.uk)
  6. That's the reason why I examine procs using the *.pro file.

    An alternative is a "full code" mode which collects all the sourcecode of a component
    and puts the "included" sourcecode after each "#include" line.

    Success, Uli

    P.S. Just incubating this scenario as part of the dIToIdO (the Idf Opener) subproject.

    Success, Uli


    Author: ulrich-merkel (ulrichmerkel@web.de)
  7. I like to see more discussions like the one above in this forum!

    Fast (11+ replies in a single day)

    and constructive replies

    and really good argumentation.


    Have a nice weekend all together,


    Author: ulrich-merkel (ulrichmerkel@web.de)
  8. Hello Ulrich

    The reasons we don't use .pro :

    • it's not possible to modify / replace codes in it.
      so since the 5.1 of uniface we developped our own search/replace tools which read / write all the model.
      (uform, uxgroup,uxfield, ucgroup , ucfield, usource etc...) and we are able in some seconds to adapt every components or objects , model to a new feature...

    Te reasons we are using .pro :

    • to check how includes are integrated in a component.

    The reasons we are using #includes :

    • Includes are undependent to objects they are included.
    •  to create a part of standard code, even it does not handle a whole entry nor operation.
      (example used in every kind of triggers, fields, entities, usource, components)
    • to create a standard operation/entry we dont want a developper could adapt.
      So the developper does not have the knowledge of how these are implemented.

    The reasons we are not using #includes :

    • Includes are undependent to objects they are included.
    • We want to let the possibility to adapt some operations in <oper>
      in such cases the operations are only pre-defined in the template as a proposal...
    • It's not possible to have a cross-reference of includes nor a tree of includes of includes etc...

    The reasons we'd like having an external entites to put Procs/operation linked to Uform :

    • to put comments into external procs / operations
    • to have the possibility to have an historic of procs
      (created through oracle triggers for example...)
    • to manage better the inheritage from the template.
      ...Even for <operation>/<defines> it's better to empty the external trigger before deriving an operation.
      you will just have the adapted operations and not a list of operations you can't know which one is derived from the template...
    • to un-include operations from components (but not dettache like #includes...)

    but we are actually satisfied with UDE and our tools, it's just a wish to increase the possibilities of managing codes.



    Author: fmoritz (francois.moritz@hewitt.com)
  9. Hekko Francois,

    thank you very much for this nice evaluation and all the different ideas you put in that posting.

    It is a great document on "Business case" on one hand and "reasons pro and con" on the other.

    I think it is worth to be put in a downloadable document.


    SUccess, Uli

    Author: ulrich-merkel (ulrichmerkel@web.de)
  10. I support Francois on this one.
    Editing huge triggers sucks,
    and include procs take too long to navigate to and you have to come up with clever names to keep track of them.
    Extra tables in DICT is the way to go.

    What do we need:

    - Global Operations
    - Component Level Operations
    - Entity Level Operations, In the Model
    - Entity Level Operations, Component Variation On
    - Occurrence Level Operations, In the Model
    - Occurrence Level Operations, Component Variation On
    - Field Level Operations, In the Model
    - Field Level Operations, Component Variation On

    Did I forget one ?

    Author: Theo Neeskens (tneeskens@itblockz.nl)
  11. Hi Theo,

    your list is all about operations.
    what about entries?

    Right now, I have problems to oversee the sourcecode of a component in the editor
    because I see only one trigger at the time.
    Scattering that code even more (without leaving a trace in the "parent sourcecode")
    makes it much harder to work in context.

    If you add new entities, you have to add another filed to the primary key.
    There you have the same situation as I faced with the include files:
    You need meaningful names.

    I see no real differences between include procs and your suggestion beside that I
    used the include procs situation very successful in the last couple of years.

    I would rather recommend the precompile directive
    to place the model codeline into my local code.

    If we could make the compiler now accept only the first definition of an entry
    (with info/warning) we have the flexibility we need.

    Success, Uli

    Author: ulrich-merkel (ulrichmerkel@web.de)
  12. Hi Francois,

    your USOURCE proposal is where the inluded procs are stored right now.
    But it is harder to get the complete picture what the sourcecode is all about
    So we need the help of *.PRO files dr similar textfiles.

    Another question:
    If more than one developer works on the code of a component,
    do they work on different aspects (UI the one, Business logic the orther)
    so we could use a "separation of concerns" for the different coding areas?

    Success, Uli

    Author: ulrich-merkel (ulrichmerkel@web.de)
  13. This is the output of the AWK-Process handling multiple *.PRO files
    Shows the location and comments of the sourcecode (would look even better if nonproportional font could be used here)

    Success, Uli

    p.s. it is a real output of a german project I dit in 2001



     Source      Entry/Operation      defined in Seq#
    WG2INI01      2O INIT_PROJEKT      For#WG2INI01 <OPER>      7
    WG2INI01      3E DO_SET_TITELTEXTE      For#WG2INI01 <LPMX>      3
    WG2INI01      3E DO_WG2LVZEILE      For#WG2INI01 <LPMX>      2
    WG2INI01      3E ERSTELL_WG2BESTVOR      For#WG2INI01 <LPMX>      4
    WG2INI01      3E LP_ERROREXIT      For#WG2INI01 <LPMX>      6
    WG2INI01      3E LP_INIT_PROJEKT      For#WG2INI01 <LPMX>      1
    WG2INI01      3E NEUES_ARBEITSLV      For#WG2INI01 <LPMX>      5
    WG2INI01      3E WG201_BREAK_LISTFELD      Ent#WG2LVZEILE.WINGABI2 <LPME>      8
    WG2INI01      3E WG201_BREAK_LISTFELD_1      Ent#WG2LVZEILE.WINGABI2 <LPME>      9
    WG2INI01      3E WG201_BREAK_LISTFELD_2      Ent#WG2LVZEILE.WINGABI2 <LPME>      10
    WG2INI02      2O NEW_ARBEITS_LV      For#WG2INI02 <OPER>      18
    WG2INI02      3E LP_ERROREXIT      For#WG2INI02 <LPMX>      17
    WG2INI02      3E LP_NEW_ARBEITS_LV      For#WG2INI02 <LPMX>      11
    WG2INI02      3E SCAN_ENTITIES      For#WG2INI02 <LPMX>      12
    WG2INI02      3E SCAN_WG2LVZEILE      For#WG2INI02 <LPMX>      13
    WG2INI02      3E SCAN_WG2PROJELE      For#WG2INI02 <LPMX>      16
    WG2INI02      3E SCAN_WG2PROJKOMB      For#WG2INI02 <LPMX>      15
    WG2INI02      3E SCAN_WG2PROJPROD      For#WG2INI02 <LPMX>      14
    WG2INI03      2O UPDATE_BESTVORS      For#WG2INI03 <OPER>      23
    WG2INI03      3E DO_AUFBRUCH_KOMBI      For#WG2INI03 <LPMX>      21
    WG2INI03      3E DO_UPDATE_BESTVORS      For#WG2INI03 <LPMX>      20
    WG2INI03      3E LP_ERROREXIT      For#WG2INI03 <LPMX>      22
    WG2INI03      3E LP_UPDATE_BESTVORS      For#WG2INI03 <LPMX>      19

    ** SEQ# 1 ** Src:WG2INI01 ** def. in:For#WG2INI01 <LPMX> **

    ; Ausführung der Operation INIT_PROJEKT
    ; mit dieser Operation wird ein aus WG1 gewandeltes Angebot
    ; initialisiert, d.h datenmäßig vervollständigt.
       string p_PROJEKT_NR : IN ; Projektnummer
       string p_lSTDINFO   : IN ; Liste mit Standard-Informationen
    ** SEQ# 2 ** Src:WG2INI01 ** def. in:For#WG2INI01 <LPMX> **
    entry DO_WG2LVZEILE
    ; vervollständigung und Check der WG2LVZEILEN
    ; 1) Belegung der Felder sTITELTEXTE, sPRODUKTNR, nMENGE
    ; 2) Prüfung auf "D"-Positionen mit Setzen von nFEST_RELPOS, sDIENSTL_LV
    ; 3) Aufbau der WG2BESTVOR-Informationen
    ; 3) Setzen von nFEST_RELPOS (= höchste RELPOS)
       string p_PROJEKT_NR : IN ; Projektnummer
       string p_lSTDINFO   : IN ; Liste mit Standard-Informationen
       string p_nFEST_RELPOS : INOUT ; Belegung p_nFESTRELPOS
       string p_sDIENSTL_LV : INOUT ; "J" = Dienstleistungs-LV
    ** SEQ# 3 ** Src:WG2INI01 ** def. in:For#WG2INI01 <LPMX> **
       string p_ZEILEN_TYP : IN         ; Zeilentyp
       string p_KZ_EBENE    : IN         ; Kennzeichen der Titelebene ("1" - "4")
       string p_LEVEL_NO    : IN         ; Text zu dieser Position
      string p_sSEP        : IN         ; Trennzeichen
       string p_s1          : INOUT      ; Speicher Text Level 1
       string p_s2          : INOUT      ; Speicher Text Level 2
       string p_s3          : INOUT      ; Speicher Text Level 3
       string p_s4          : INOUT      ; Speicher Text Level 4
       string p_sTITELTEXTE : OUT        ; Ausgabe: Feld für Titeltexte


    Author: ulrich-merkel (ulrichmerkel@web.de)
  14. More importantly to me is that you should be able to detach only on operation/entry from the template and leave the others intact and still viewable from the main source code.

    If I change one character in any operation, the entire trigger is detached and I have the alternatives of deleting all the other operations from the trigger, and allowing them to inherit but not being able to see their contents or leaving them there and preventing inheritance.

    Separate records for each operation would allow for sensible maintenance of which operations are detached....

    Author: Iain Sharp (i.sharp@pcisystems.co.uk)
  15. Hello all,

    my 2 cents:

    - operations - attached, detached. As far as I know you can do this.

    If there are operations in the model defined (e.g.); you comment out the operations-trigger (external), the operations are still available. If you want to do an external variation on one operation, just put this single NEW operation into the operation-Trigger and this overrides the specific operation, as defined in the model.
    The rest is still conceptual defined and (look in the .pro File) available.

    AFAIK, it works only with operations-Trigger.

    Some of the wished functionality seemes to be available in 9.3. The good old no-where used "Cross reference" is reactivated. I didn't test it, but it seemes to have some of discussed functionality. I saw somewhere ( I think it was 9.4.Beta) a "global search", which seemes to be quite fast and good.

    I am at least missing the possibility to jump direct to the specific piece of code from:
    - *.pro File results
    - search results
    - from errors and warnings in the message-frame
    - from a call or activate-Statement.
    - out of the debugger ;-)
    and some other places...



    Author: gypsilon (wva@gypsilon.de)
  16. attached, please find an article from the german usergroup.

    Provides you with a lot of information and it is completely up to you what you enter as "sourcecode".
    Perhaps a concatenation of all your "working areas"

    Success, Uli

    This work is based on the article in the c-b-g.org Newsletter 03/07 page 9:
    3.3.2. $listoperations (by Dominik Michel)
    parse_result = $listoperations (sourcecode)
    parse_result is a list-in-list construct; each element of the main list has the following structure:
     1. all in front of "entry" or "operation"
     2. "partner" if applicable
     3. "entry" or "operation"
     4. name of routine
     5. the inline comment of the routine
     6. variableType of "returns"
     7. the inline comment of "returns"
     8. all after "entry" and before "params" or "variables"
     9*. a list per parameter:paramType;paramName;direction; the inline comment
    n-1. all after "entry" including the "params"-Block
     n. all after the previous "entry + params" block
    The beauty is the fact that all inline comments are captured as well.
    So more vital information than the "naked" parameter details can be given to the user.

    Author: ulrich-merkel (ulrichmerkel@web.de)
  17. hello

    it's very shame it'd not be used at runtime like this  :

    vlist = $listoperations($instancename)

    So we could know before etablishing a dialogue with an instance if it could answer...

    that's I need.



    Author: fmoritz (francois.moritz@hewitt.com)
  18. ... for this I added a feature "-listcontracts-" to all my components (still following the DbC)
    which returns the "public" methods as a uniface list.

    Success, Uli


    Author: ulrich-merkel (ulrichmerkel@web.de)
  19. I used to go to great lengths to preserve inheritance from a template by having both stable and editable contents of e.g. an OPER trigger - consider:


    ;(all the template operations are here)


    #ifdefined HAS_OPER

    #include functions:<$componentname>_OPER


    I had a 'additional menu' shortcut to open the #include functions:<$componentname>_OPER for editing (or just a 2nd IDF open in that library)

    But now I bother less since I notice that the template operations get inherited from the template even if the developer completely overwrites the trigger.

    If a new definition of a template operation gets written then it gets overlayed - as desired

    This is only true for operations.  If you overwrite the OPER trigger then any entries defined in the template are lost.

    But you can define template entries in another trigger like <RETS> that no one ever uses ?

    KISS !

    Author: AyeJayUU (awilkins1@btinternet.com)