Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

So I was looking at the serialization classes

93 views
Skip to first unread message

flicker...@anduin.net

unread,
Mar 2, 2021, 11:50:26 AM3/2/21
to
So I was looking at serialization classes ....
and I had some issues with the classes provided
so I wrote something which would serialize any class

serialized rexx object class

/*
Serializer
*/

::Class SerializeHandler public
::constant code
::attribute classHandled
::attribute seperator private
::constant sepcode "!"
::attribute dataEncodeMethod
::attribute dataDecodeMethod

::constant seperatorwordpos 1
::constant commandwordpos 2
::constant idwordpos 3
::constant datawordpos 4




::Method serialize public
use arg object, helper

return self~serializer(object, helper)

::method deserialize public
use arg data, helper
return self~deserializer(data, helper)

::method Accepts
use arg classQuery
response = .false
if classQuery~class = self~classHandled then response = .true
return response

::method AcceptsCommand
use arg command
if command = self~code then response = .true
else response = .false
return response

::method setSepcode
use arg helper
self~seperator = self~sepcode || helper~referencesBag~items()
return self~seperator

::method endcode
return self~code||"END"

::method getSeperator
use arg data
select
when data~class = .RTOSerializeService then do
returnobject = self~setSepcode(data)
end
otherwise do
returnobject = data~word(self~seperatorwordpos)
end
end
return returnobject

::method getElements
use arg data
delim = self~getSeperator(data)

elem = .array~new()

do i = 0 by 1 while data <> ''
parse var data w.i (delim) data
elem~insert(w.i)
end
return elem

::method makeObjectClassHandled
o = self~classHandled~new()
return o

/* abstract methods */
::method serializer abstract

/* abstract methods */
::method deserializer abstract




::Class ArraySerializeHandler subclass SerializeHandler public
::constant code ARR
::constant sepcode "|"

::method init
self~classHandled = .Array

::method serializer
use arg object, helper

currentseperator = self~getSeperator(helper)
data = self~code helper~id(object) object~items
do i = 1 to object~items
data = data currentseperator helper~runSerializer(object~at(i))
end
data = currentseperator data currentseperator self~endcode helper~id(object)
return data

::method deserializer
use arg object, helper

ArraySize = object~word(4)

ArrayReturnObject = .Array~new(ArraySize)

findcode = self~endcode helper~getserializedID(object)

parse var object object (findcode) junk

elements = self~getElements(object)


/* object being registered and the string which caused it to be registered
from which we will work out what the ID which is being used is going to be
*/
helper~deserializedObjectReferencesDirectory~put(ArrayReturnObject,object~word(3))



do e over elements~allitems
if e <> .nil & e <> "" then do
o = helper~runDeserializer(e)

ArrayReturnObject~append(o)
end

end

return o


::Class LikeArraySerializeHandler subclass SerializeHandler public
::method serializer
use arg object, helper

helper~addReference(object)

currentseperator = self~getSeperator(helper)

items = object~makeArray
data = currentseperator self~code helper~id(object) items~items


do i = 1 to items~items
if object~at(items~at(i)) <> .nil then do
data = data currentseperator helper~runSerializer(object~at(items~at(i)))
end
end
data = data currentseperator self~endcode helper~id(object)
return data


::method deserializer
use arg object, helper
ro = self~classHandled~new()
parse var object object (findcode) junk

elements = self~getElements(object)

/* object being registered and the string which caused it to be registered
from which we will work out what the ID which is being used is going to be
*/
helper~deserializedObjectReferencesDirectory~put(ro,object~word(3))


do i over elements~allindexes



e = elements~at(i)
o = helper~runDeserializer(e)
if o~class <> .Object then do
ro~put(o,i)
end


end



return o


::Class BagSerializeHandler subclass LikeArraySerializeHandler public
::constant code BAG
::constant sepcode "^"

::method init
self~classHandled = .Bag

::method deserializer
use arg object, helper
ro = self~classHandled~new()
parse var object object (findcode) junk

elements = self~getElements(object)

/* object being registered and the string which caused it to be registered
from which we will work out what the ID which is being used is going to be
*/
helper~deserializedObjectReferencesDirectory~put(ro,object~word(3))


do i over elements~allindexes

e = elements~at(i)
o = helper~runDeserializer(e)
if o~class <> .Object then do
ro~put(o,o)
end

end



return ro


::Class DirectorySerializeHandler subclass LikeArraySerializeHandler public
::constant code DIR
::constant sepcode "*"

::method init
self~classHandled = .Directory


::Class ListSerializeHandler subclass LikeArraySerializeHandler public
::constant code LIS
::constant sepcode "*"

::method init
self~classHandled = .List



::Class StringSerializeHandler subclass SerializeHandler public
::constant code STR
::method init
self~classHandled = .String

::method serializer
use arg object, helper
currentseperator = self~getSeperator(helper)
helper~addReference(object)
return currentseperator self~code helper~id(object) object~encodeBase64

::method deserializer
use arg object, helper


/* RO = self~makeObjectClassHandled() */
RO = object~word(self~datawordpos)~decodeBase64

/* object being registered and the string which caused it to be registered
from which we will work out what the ID which is being used is going to be
*/
helper~deserializedObjectReferencesDirectory~put(RO,object~word(2))

return RO


::Class ExternalClassSerializeHandler subclass SerializeHandler public
::constant code VAL
::constant sepcode ":"
::constant commandwordpos 2
::constant seperatorwordpos 1

::method serializer
use arg objectToInspect, helper

currentseperator = self~getSeperator(helper)

serializedata = ""

classOfObjectInspected = objectToInspect~class()
allClassesToBeInspected = helper~reverseList(helper~findSubclasses(classOfObjectInspected, .list~new()))

serializedata = currentseperator "OBJ" helper~id(objectToInspect) helper~classname(objectToInspect) helper~id(classOfObjectInspected) helper~classname(classOfObjectInspected) currentseperator

methodProvider = .relation~new
do classToCheck over allClassesToBeInspected
methodProvider = helper~findMethodsAndProviders(classToCheck, methodProvider)
end

do classToCheck over allClassesToBeInspected
if classToCheck~package~name="REXX" then do
nop
end
else
do
serializedata = serializedata currentseperator "SUBC" helper~id(objectToInspect) helper~classname(classToCheck) helper~id(classToCheck) currentseperator

methodsinClass = methodProvider~allAt(classToCheck)

serializedata = serializedata "CODE" helper~id(objectToInspect) helper~classname(objectToInspect) currentseperator
do method over methodsinClass
if helper~hasSource(classToCheck, method) = .true then do
source = helper~getSource(classToCheck, method)
serializedata = serializedata "SRC" helper~id(objectToInspect) method helper~classname(classToCheck) helper~id(classToCheck) helper~runSerializer(source~makeString) currentseperator
end
end
serializedata = serializedata "CODEEND" helper~id(objectToInspect) currentseperator


serializedata = serializedata "ATTR" helper~id(objectToInspect) helper~classname(objectToInspect) currentseperator
do method over methodsinClass
if helper~hasSource(classToCheck, method) = .false then do
if method <> "INIT" then do
if helper~isSettor(method) = .false then do
AttrValue = objectToInspect~sendWith(method,.Array~new)
serializedata = serializedata self~code helper~id(objectToInspect) method helper~runSerializer(AttrValue) currentseperator
end
end
end
end
end

end
serializedata = serializedata "ATTREND" helper~id(objectToInspect) currentseperator
return serializedata "OBJEND" helper~id(objectToInspect)

::method AcceptsCommand
use arg command
select
when command = self~code then response = .true
when command = "OBJ" then response = .true
when command = "CODE" then response = .true
when command = "ATTR" then response = .true
when command = "SRC" then response = .true
when command = "SUBC" then response = .true
otherwise response = .false
end
return response

::method makeObjectClassHandled

return .nil

::method Accepts
use arg classQuery
response = .true
if classQuery~class~package~name="REXX" then response = .false
return response

::method deserializer

use arg object, helper

findcode = self~endcode helper~getserializedID(object)

parse var object object (findcode) junk

elements = self~getElements(object)

ReturnObject = .Object~new


/* object being registered and the string which caused it to be registered
from which we will work out what the ID which is being used is going to be
*/
/* helper~registerDeserializedObject(ReturnObject, elements~at(2)) */

/* remove item from the list of elements */

ValuesToSet = .Directory~new

parentClass = .object


do e over elements~allitems
if e <> .nil & e <> "" then do
command = e~word(1)
id = e~word(2)
methodorclass = e~word(3)

select
when command = "OBJ" then do
ReturnObject=.object~subClass(methodorclass)~new()
helper~deserializedObjectReferencesDirectory~put(ReturnObject,id)

end

when command = "SUBC" then do
parentClass = parentClass~class~subclass(e~word(3))
end
when command = "SRC" then do
parse var e word1 word2 word3 word4 word5 rest
methodname = methodorclass
src = helper~runDeserializer(rest)
src = src~changestr(x2c("0A"),";")
mymethod= .method~new("",src)
returnObject~class~define(methodname,mymethod)
end
when command = "VAL" then do


ReturnObject~class~subclass(parentClass~class~id)


parse var e word1 word2 word3 rest
o = helper~runDeserializer(rest)
helper~deserializedObjectReferencesDirectory~put(o,id)

variabletosend = .Array~new
variabletosend~append(o)


/* create settor */
methodname = methodorclass||"="
src = .Array~new
src~append("expose "||methodorclass)
src~append("use arg "||methodorclass)
mymethod= .method~new("",src)
ReturnObject~class~define(methodname,mymethod)

ValuesToSet~put(variabletosend, methodname)

/* create getter */
methodname = methodorclass
src = .Array~new
src~append("expose "||methodorclass)
src~append("return "||methodorclass)

mymethodA = .method~new("",src)
ReturnObject~class~define(methodname,mymethodA)

ReturnObject= ReturnObject~class~subclass(ReturnObject~class~id)~new


end
otherwise do
end

end

end

end

/* Set Values */

sindexes = ValuesToSet~Supplier~allIndexes
sitems = ValuesToSet~Supplier~allItems
do i = 1 to sindexes~items
index = sindexes~at(i)
v = sitems~at(i)
returnObject~send(index,v)
end

return ReturnObject





/*
Because one needs a service

*/

::CLASS RTOSerializeService public
::attribute SerializeHandlers
::attribute referencesBag
::attribute SerializeSource
::attribute deserializedObjectReferencesDirectory


::METHOD INIT public

self~SerializeHandlers= .list~new()

self~register(.StringSerializeHandler~new())
self~register(.ArraySerializeHandler~new())
self~register(.BagSerializeHandler~new())
self~register(.DirectorySerializeHandler~new())
self~register(.ListSerializeHandler~new())
self~register(.ExternalClassSerializeHandler~new())


self~reset



::method register
use arg obj
sh = self~SerializeHandlers()
sh~insert(obj, sh~last)

::method reset
self~referencesBag=.bag~new
self~deserializedObjectReferencesDirectory = .Directory~new


::method Serialize public
use arg object
returned = self~runSerializer(object)
self~reset()
return returned



::method runSerializer
use arg objectBeingInspected

serializedata = "REF" self~id(objectBeingInspected)

if self~hasReference(objectBeingInspected) = .false then do
self~addReference(objectBeingInspected)
do handler over self~SerializeHandlers
if handler~Accepts(objectBeingInspected) = .true then do
serializedata = handler~serialize(objectBeingInspected, self)
end
end


end
return serializedata

::method Deserialize public
use arg input
output = self~runDeserializer(input)
self~reset()
return output


::method runDeserializer
use arg input


output = .Object~new


if input~word(1) = "REF" then do
output = self~deserializedObjectReferencesDirectory~at(input~word(2))
self~deserializedObjectReferencesDirectory~put(output, input~word(2))
end
else
do handler over self~SerializeHandlers
command = input~word(handler~commandwordpos)
if handler~AcceptsCommand(command) = .true then do
output = handler~deserialize(input, self)
self~deserializedObjectReferencesDirectory~put(output, input~word(3))
output = self~deserializedObjectReferencesDirectory~at(input~word(3))
end
end



return output


/*
Recursively find subclasses
*/

::method findSubclasses
use arg classtoFind, list
if classToFind <> .nil then do
/* put it in the list if it isn't */
if list~hasitem(classtoFind) = .false then do
list~insert(classtoFind,list~last)
end
self~findSubclasses(classtoFind~superclass, list)
end
return list

/*
find methods against each class
*/


::method findMethodsAndProviders
use arg classtosearch, methodRel

foundMethods = classtosearch~methods(classtosearch)

do while foundMethods~available
methodfound = foundMethods~index
methodRel[classtosearch] = methodfound
foundMethods~next
end

return methodRel

::method addReference
use arg obj
self~referencesBag~put(obj)

::method hasReference
use arg obj
return self~referencesBag~hasitem(obj)


/*
Does it have source code
*/

::method hasSource
use arg classToInspect, MethodName
hasSource = .false
if classToInspect~hasMethod(MethodName) = .false then do
source = self~getSource(classToInspect, MethodName)
if source~isEmpty() then hasSource = .false
else hasSource = .true
end
return hasSource

::method getSource
use arg classToInspect, MethodName
source = classToInspect~method(MethodName)~source
return source

/* provide class name */

::method className
use arg object
return object~defaultname~word(2)

/* provide ID */

::method id
use arg object
return object~identityHash

/*
The ID in the serialized form
*/
::method getserializedID
use arg object

return object~word(2)

::method isSettor
use arg object
response = .false
if object~pos("=") > 1 then response = .true
return response

::method registerDeserializedObject
use arg object, serializedForm
id = self~getserializedID(serializedForm)
self~deserializedObjectReferencesDirectory~put(object,id)

::method reverselist
use arg Inputlist
/* Reverse the list order */
OutputList = .list~new()

do until Inputlist~isEmpty
OutputList~append(Inputlist~lastItem)
Inputlist~remove(Inputlist~last)
end

return OutputList





bank account cls

/*
Bank Account Demo
*/




::class bankaccount subclass object public
::attribute balance
::attribute interestrate
::attribute waittime
::attribute feesToBeCharged
::attribute transactionFee
::attribute accountActive
::attribute daySinceOpen
::attribute ledger
::attribute AccountHolder
::constant SecretCode 299792458


::method init public
self~ledger = .Array~new()
self~waittime = 3
self~AccountHolder = .Directory~new

::method openAccount public
use arg initialDeposit
self~balance = 0
self~feesToBeCharged = 0 /* First one is free .. how generous */
self~interestrate = 0.00127
self~transactionFee = 2.50
self~accountActive = .true
say "Account opened"
self~daySinceOpen = 0
self~transaction(initialDeposit)

::method addInterest public
interest = trunc(self~balance * self~interestrate,2)
self~recordTransaction(interest)
self~balance = self~balance + interest

::method transaction public
use arg amount
self~recordTransaction(amount)
self~balance = self~balance + amount
self~feesToBeCharged = self~feesToBeCharged + self~transactionFee
say "did transaction for" amount "balance" self~balance "and fees due" self~feesToBeCharged

::method chargeFees public
self~balance = self~balance - self~feesToBeCharged
say "Fees charged" self~feesToBeCharged
self~recordTransaction(self~feesToBeCharged)
self~feesToBeCharged = 0

::method recordTransaction
use arg amount
self~ledger~append(amount)

::method nukeWorld public
use arg secretcode
say "you have accidentally pressed the button and destroyed the world"
return

::method LinkAccountHolder public
use arg CustomerToLink
self~AccountHolder~put(CustomerToLink,CustomerToLink~name)
say self~AccountHolder~items
CustomerToLink~account~put(self)


::class SavingsAccount subclass bankaccount public

::method openAccount public
use arg initialDeposit
/* super it first */
self~openAccount:super(initialDeposit)
/* then change some settings */
self~interestrate = 0.00427
self~transactionFee = 5.50

::class SuperSavingsAccount subclass SavingsAccount public

::method openAccount public
use arg initialDeposit
/* super it first */
self~openAccount:super(initialDeposit)
/* then change some settings */
self~interestrate = 0.00427
self~transactionFee = 5.50



::class BankCustomer public subclass object
::attribute name public
::attribute address public
::attribute account public

::method init public
self~account = .bag~new

test ckass
/*

*/

use arg fn

SerializeService = .RTOSerializeService~new()

if fn <> "FN" then do
data = linein(fn)
data = SerializeService~Deserialize(DATA)
say data~balance
say data~AccountHolder~items
say data~ledger~items
end
else
do



MyBankAcct = .SuperSavingsAccount~new()




/*
It's something external
*/

maxtransactionstorun = random(3,7)

i = 1

BobBobskin= .BankCustomer~new()
BobBobskin~name="Bob Bobskin"
MyBankAcct~LinkAccountHolder(BobBobskin)

Dashinka = .BankCustomer~new()
Dashinka~name="Dashika Doodie"
Dashinka~address = "1234 street"
MyBankAcct~LinkAccountHolder(Dashinka)

SerializeService~SerializeSource = .true




a = SerializeService~Serialize(Dashinka)
b = SerializeService~deserialize(a)
SerializeService~RESET





do forever
if MyBankAcct~accountActive = .true then do

say MyBankAcct~accountActive

MyBankAcct~daySinceOpen = MyBankAcct~daySinceOpen + 1
say "On day" i "Account Open for" MyBankAcct~daySinceOpen "Days, Balance" MyBankAcct~balance

MyBankAcct~addInterest()

/* Run a random number of transactions */
do j = 1 to RANDOM(5)
MyBankAcct~transaction(RANDOM(-10,10))
end
i = i + 1
if (MyBankAcct~daySinceOpen // 7) = 0 then MyBankAcct~chargeFees

say "ledger has" MyBankAcct~ledger~items()
say copies("-",20)



if i = 10 then do
data = SerializeService~Serialize(MyBankAcct)
rc = lineout("serialized.txt",data)
SerializeService~reset
end










end
else do
if random(0,5) = 1 then do
MyBankAcct~openAccount(100)
end
end
say "waiting" i
call SysSleep(MyBankAcct~waittime)

end


end


::requires "rto.cls"
::requires "bankaccount.cls"

JL Turriff

unread,
Jun 2, 2021, 9:02:36 AM6/2/21
to
When I try to run this I get an error:

$ rexx SerializedRexxObjectClass
878 *-* ::requires "bankaccount.cls"
Error 43 running /home/leslie/bin/rexx/SerializedRexxObjectClass line 878: Routine not found.
Error 43.901: Could not find file "bankaccount.cls" for ::REQUIRES.
@07:25:20,leslie@pinto rc=213

It looks to me like the file might need to be broken into several pieces, but it's not clear where the breaks should be.

Jean-Louis Faucher

unread,
Jun 2, 2021, 9:46:22 AM6/2/21
to
rto.cls : cut before the comment Bank Account Demo
bankaccount.cls : cut after the class BankCustomer
test_ckass.rex : the rest

I stopped the demo when the ledger became > 600 :-)
A file serialized.txt was generated.
The serialization of MyBankAcct is only on day 10.
At each run, a new line is added to serialize.txt.

flicker...@anduin.net

unread,
Jun 3, 2021, 8:18:00 AM6/3/21
to
sounds about right... it was a little demo.
0 new messages