Subscribe for automatic updates: RSS icon RSS

Login icon Sign in for full access | Help icon Help
Advanced search

Pages: [1]
  Reply  |  Print  
Author Topic: Reverse of base.typeinfo.create()?  (Read 15891 times)
Snorri B.
Posts: 103


« on: December 22, 2008, 03:37:13 pm »

Hi.

Using base.TypeInfo.create allows you to write a generic function that accepts "any kind" of record (or array). Example:
Code
  1. define myrec record
  2.  a int,
  3.  b int,
  4.  c char
  5. end record
  6.  
  7. call myfunc(base.typeinfo.create(myrec))
  8. .
  9. .
  10.  
  11. function  myfunc(p)
  12. define p om.Domnode
  13. -- Do something
  14. end function
  15.  

So far so good, you can call myfunc with any type of myrec.* without braking anything (myrec could be an array in another function). What I would like to be able to do is to serialize the Domnodes back to variables, something like:

Code
  1. define myrec record
  2.  a int,
  3.  b int,
  4.  c char
  5. end record
  6.  
  7. call myfunc(base.typeinfo.create(myrec)) RETURNING myrec.*
  8. .
  9. .
  10.  
  11. function  myfunc(p)
  12. define p om.Domnode
  13. -- Do something
  14. RETURN p.SerializeToVariables()
  15. end function
  16.  

What do you think? IMO base.typeinfo.create() only does half of the job!


Best regards,
-Snorri
Snorri B.
Posts: 103


« Reply #1 on: December 30, 2008, 10:50:51 am »

Hi.

Just wandering if somebody from 4js is reading this?

Best regs & marry xmas,
-Snorri
Sebastien F.
Four Js
Posts: 509


« Reply #2 on: December 30, 2008, 07:05:13 pm »

Hello Snorry,

We read all topics and take all requests into account, but we need to discuss a bit internally before giving an answer.

Maybe we can do something in 2.20, with the new FglRecord class... not sure, need to talk with others.

Could you give us more details about your need?
Why do you want to serialize / unserialize records / arrays?

Happy new year 2009!
Seb
Snorri B.
Posts: 103


« Reply #3 on: January 02, 2009, 03:40:20 pm »

Hi Seb and a happy new year.

The reasons for serializing can be a few. In our application, we store program variables for various reasons, for example Report setup (basically a screen record) or Setup for batch jobs.
Example:

define myrec record
 a char, b char, c int
end record

input by name myrec.* without defaults attributes(unbuffered)
 on action get_stored_settings call get_stored("thisapp", base.typeinfo.create(myrec)) returning myrec.*
 on action store_settings call store_settings("thisapp", base.typeinfo.create(myrec))
end input

I hope you get the idea. The store_settings routine walks thru the domnode and inserts the USER, appname, variable and value into a table. Writing get_stored is trickier, but we do this by getting the name of variables from the domnode, (and counting how many variables need to be returned), reading the stored settings table into an array and then we need to

case number_of _columns
when 1 return a[1]
when 2 return a[1],a[2]
when 3 return a[1],a[2],a[3]
when 4 --- etc. etc
end case

This is however impossible to write if myrec is an array. So basically, allowing serialization of records/arrays would give you the ability to write (complex maybe) generic routines that could return the same structure they are called with.

Does this make sense?

Best regards,
-Snori
Hello Snorry,

We read all topics and take all requests into account, but we need to discuss a bit internally before giving an answer.

Maybe we can do something in 2.20, with the new FglRecord class... not sure, need to talk with others.

Could you give us more details about your need?
Why do you want to serialize / unserialize records / arrays?

Happy new year 2009!
Seb
Sebastien F.
Four Js
Posts: 509


« Reply #4 on: January 03, 2009, 09:44:22 am »

Hello Snorri,

Yes I understand the need.
I had a similar suggestion to save/restore search criterion in CONSTRUCT instructions.

Be sure that describing your needs in detail will help to make things happen.

I have just filed a new bug/enhancement (#11726)

Thanks,
Seb
Reuben B.
Four Js
Posts: 1062


« Reply #5 on: January 05, 2009, 03:16:11 am »

Hi Seb,

I hopefully had a request in the system dating back to the time when I was a customer which was trying to achieve the same end result that Snorri was trying to achieve.  That is storing away current field values, and being able to get those values back and re-apply them.

I was trying to get to a point  (I'll use the expression field_value instead of stored_settings to avoid confusion with the GDC stored settings) of having 2 actions ...

Code
  1. ON ACTION save_field_values
  2.   CALL save_field_values()
  3.  
  4. ON ACTION get_field_values()
  5.   CALL get_field_values()
... in every INPUT and CONSTRUCT, (I am not sure what my intention was with INPUT ARRAY).  Note that the function calls do not need to reference the variables used in the dialog statement.  If a new field was added I didn't want these function calls to have to be altered. 

To save the current field values 'was' achievable with code similar to ...

Code
  1. LET l_root = ui.Interface.getRootNode()
  2. LET l_xpath = "//Dialog[@active=\"1\"]"
  3. LET l_dialog_list = l_root.selectByPath(l_xpath)
  4. FOR i = 1 To l_dialog_list.getlength()
  5.   LET l_dialog_node = l_dialog_list.item(i)
  6.   LET l_xpath = "//FieldInfo"
  7.   LET l_fieldinfo_list = l_dialog_node.selectByPath(l_xpath)
  8.   FOR j = 1 TO l_fieldinfo_list.getLength()
  9.      LET l_fieldinfo_node = l_fieldinfo_list.item(j)
  10.      LET l_nodeIdRef = l_fieldinfo_node.getAttribute("nodeIdRef")
  11.      LET l_field_node = get_node_by_id(l_nodeIdRef)
  12.      LET l_name = l_field_node.getAttribute("name")
  13.      LET l_value = l_field_node.getAttribute("value")
  14.      CALL add_to_list(l_name, l_value)
  15.   END FOR
  16. END FOR
... note I say 'was', I haven't checked if this still works in 2.1x and multi-dialog in particular.

To read the values and apply them was then going to be something like ...

Code
  1. LET l_root = ui.Interface.getRootNode()
  2. LET l_xpath = "//Dialog[@active=\"1\"]"
  3. LET l_dialog_list = l_root.selectByPath(l_xpath)
  4. FOR i = 1 To l_dialog_list.getlength()
  5.   LET l_dialog_node = l_dialog_list.item(i)
  6.   LET l_xpath = "//FieldInfo"
  7.   LET l_fieldinfo_list = l_dialog_node.selectByPath(l_xpath)
  8.   FOR j = 1 TO l_fieldinfo_list.getLength()
  9.      LET l_fieldinfo_node = l_fieldinfo_list.item(j)
  10.      LET l_nodeIdRef = l_fieldinfo_node.getAttribute("nodeIdRef")
  11.      LET l_field_node = get_node_by_id(l_nodeIdRef)
  12.      LET l_value = read_from_list(l_name)
  13.      CALL l_field_node.setAttribute(name”, l_value)
  14.   END FOR
  15. END FOR
  16. CALL ui.Interface.XXX()

... but I found I needed a function (the XXX in the code above) to set the underlying 4GL variables equal to the value attribute I had overwritten in the DOM tree.

If I was looking at this again, I'd have to consider multi-dialog and figure out how to include arrays, both input and display (single and multi-row select) in what is stored away and set back.

Reuben

Product Consultant (Asia Pacific)
Developer Relations Manager (Worldwide)
Author of https://4js.com/ask-reuben
Contributor to https://github.com/FourjsGenero
Sebastien F.
Four Js
Posts: 509


« Reply #6 on: January 05, 2009, 01:41:54 pm »

Reuben,

This is interesting code... However, I would not recommend to set field values this way, because the reference for field values are the program variables, especially when using the UNBUFFERED mode. So to me this would be a "use at your own risks" code.

I believe that this code was working for CONSTRUCT, since only form field buffers are used in this instruction.

Seb
Reuben B.
Four Js
Posts: 1062


« Reply #7 on: January 05, 2009, 10:29:52 pm »

Seb,

Don't worry, this code never made it to production as it never worked.  We were missing the CALL ui.Interface.XXX() to update the 4GL variables with the value of the corresponding value attribute in the DOM tree.

Without such a routine we had to hand-code the save and get of field values e.g.

INPUT field1, field2, field3 ...

ON ACTION save_field_values
   # some header
   # save field1 value
   # save field2 value
   # save field3 value
   # some trailer

ON ACTION get_field_values
   # some header
   LET field1 = # get saved value for field1
   LET field2 = # get saved value for field2
   LET field3 = # get saved value for field3

... all of which meant that this functionality
a) was only applied to certain screens
b) any change to those screens in terms of fields being added/removed meant the save and get routines had to be edited.

The key thing from Snorris comment was "ability to write (complex maybe) generic routines".  An ISV developer doesn't want to have to hand-code the same thing over and over again.  Snorri was looking for an extension to base.TypeInfo, I was looking for an extension to ui.Interface, to effectively end up with the same generic result, that is to change the value of a variable without necessarily typing that variable into the code.  Maybe we could achieve what we want with LET variable() = value() ?

Product Consultant (Asia Pacific)
Developer Relations Manager (Worldwide)
Author of https://4js.com/ask-reuben
Contributor to https://github.com/FourjsGenero
Snorri B.
Posts: 103


« Reply #8 on: January 06, 2009, 12:05:22 pm »

Reuben, thank you very much for your input.
We started out using a similar method as you but encountered the same problems.
I think we made a point here and regardless of the implementation I think it would be of great benefit to the language to add this feature.
I looked at the fglrecord class 2.20 doc and it seems to have the same limitations (as base.typeinfo), you can pass it to a function, but not return it.

Best regards,
-Snorri
David H.
Posts: 158


« Reply #9 on: January 07, 2009, 01:10:40 pm »

Hi Snorri,

I'm a bit late to this thread but was wondering if you'd looked at the XML Serializer class of the Web Services XML Extension Library. I have no experience of using these classes myself, but in principal I don't see why you could not use them in your BDL app. Or am I missing something here?

xml.Serializer.VariableToDom(var VARIABLE,node xml.DomNode )    

xml.Serializer.DomToVariable(node xml.DomNode,var VARIABLE )

https://4js.com/techdocs/genero/gws/devel/DocRoot/User/WseXmlSerializer.html#SerializerOptions

Regards,

David
Snorri B.
Posts: 103


« Reply #10 on: January 07, 2009, 02:50:08 pm »

Hi David and thanks for your input.

Yes, we thought about using xml.Serializer, and it can be done but it has some unfortunate side effects. First of all, it is not possible to use "normal" record declarations you need to specify XML attributes for the serializer to work like for example attribute(XMLOptional) or XMLName etc. which puts extra burden on the person calling your routine. It also requires additional lines of code and complexity.

Example if base.typeinfo could be used:

define ktolur record
    ktala    varchar(10),
    veflyk  varchar(30),
    drasl dynamic array of record
        d1 char(1),
        d2 char(10)
    end record
  end record

call my_generic_func(base.typeinfo.create(ktolur)) returning ktolur.*
...

Example using the serializer (bold is additional code):

type t_ktolur record
    ktala       varchar(10),
    veflyk  varchar(30),
    drasl dynamic array of record attribute(XMLOptional)
        d1 char(1),
        d2 char(10)
    end record
  end record

define ktolur t_ktolur
define p1, p2 xml.DomNode
call xml.Serializer.VariableToDom(ktolur, p1)

call my_generic_func(p1) returning p2
call xml.Serializer.DomToVariable(p2, ktolur)
...
As you can see, this is much more complex than using base.typeinfo (if that would be possible).

Best regards,
-Snorri
Reuben B.
Four Js
Posts: 1062


« Reply #11 on: January 08, 2009, 12:09:07 am »

Thanks for the tip also David.  You gave me some false hope but I encountered the fact that the variable needs to be known.  I can't have the variable determined at run-time e.g.

Code
  1. CALL xml.Serializer.DomToVariable(dom, function_to_return_a_variable())

I didn't run into the issue with XML attributes  that Snorri did (I only ran it in 2.20).  This code appeared to run OK

Code
  1. IMPORT xml
  2.  
  3. # this would probably be in an include file with r,c named more uniquely
  4. DEFINE r,c xml.DomNode
  5. &define CALL_save_variable_to_dom(variable, dom) LET dom = xml.DomDocument.createDocument(#variable) \
  6.                                            LET r = dom.getDocumentElement() \
  7.                                            CALL xml.Serializer.variabletodom(variable, r)
  8.  
  9. &define CALL_restore_dom_to_variable(dom, variable) LET r = dom.getDocumentElement() \
  10.                                               LET c = r.getLastChild() \
  11.                                               CALL xml.Serializer.domtovariable(c, variable)
  12.  
  13. DEFINE mydomdoc xml.DomDocument
  14. DEFINE myrec RECORD
  15.   a INTEGER,
  16.   arr DYNAMIC ARRAY OF RECORD
  17.      x, y INTEGER
  18.   END RECORD
  19. END RECORD
  20.  
  21. MAIN
  22.  
  23.   # Populate the record
  24.   LET myrec.a = 1
  25.   LET myrec.arr[1].x = 2
  26.   LET myrec.arr[1].y = 3
  27.   LET myrec.arr[2].x = 4
  28.   LET myrec.arr[2].y = 5
  29.   CALL display_myrec("Initial values")
  30.  
  31.   # Save it away
  32.   CALL_save_variable_to_dom(myrec, mydomdoc)
  33.  
  34.   # Clear out the variable
  35.   INITIALIZE myrec.* TO NULL
  36.   CALL myrec.arr.clear()
  37.  
  38.   # verify that the record is cleared
  39.   CALL display_myrec("Record has been cleared")
  40.  
  41.   # Get it back
  42.   CALL_restore_dom_to_variable(mydomdoc, myrec)
  43.  
  44.   # verify that the record has been restored
  45.   CALL display_myrec("Record has been restored")
  46. END MAIN
  47.  
  48. FUNCTION display_myrec(title)
  49. DEFINE title STRING
  50.  
  51. &define show(p1) DISPLAY #p1 || " = ", p1
  52.   DISPLAY title
  53.   show(myrec.a)
  54.   show(myrec.arr.getlength())
  55.   IF myrec.arr.getLength() > 0 THEN
  56.      show(myrec.arr[2].y)
  57.   END IF
  58.   DISPLAY ""
  59. END FUNCTION




Product Consultant (Asia Pacific)
Developer Relations Manager (Worldwide)
Author of https://4js.com/ask-reuben
Contributor to https://github.com/FourjsGenero
Neil M.
Four Js
Posts: 21


« Reply #12 on: January 28, 2009, 05:27:32 pm »

I raised this a long time ago from a different angle.

The need was for a dynamic maintenance program, ie the program that became my DBQuery program.

The only thing the DBQuery program really lacks is the ability to UPDATE the records it browses.
The reason I can't do INPUT rec.* FROM rec.* is because for INPUT the field type/size information
is always taken from the runtime system, ( except for CONSTRUCT ) so basically the GDC would treat
all my fields as I'd defined them ( all CHAR(255) ).

Now if I could serialize xml into a record of the correct type of fields then I could do the INPUT and the
GDC would treat all the fields as the correct type and size.

Then basically we'd only ever have to write one maintenance program!

Now that we have the work done for web services for something close to this then hopefully some of the
technical issues involved have been solved. :)
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines