RPGle - i wish i could do some 'Reflection' using RPGLe - reflection

I wish i could do some reflection using RPGLe.
By reflection, I mean :
'The process or mechanism of determining the capabilities of an object at run-time.'
Imagine you have this datastructure :
D DS_Format DS Qualified Based(pDS_Format)
D Type 20I 0 Inz(1)
D Label 50A Inz('myLabel')
D Description 5000A Inz('myDescription')
With a reflection api, I could do this :
Reflection_ListSubfields(DS_Format);
=> return this array : { 'Type', 'Label', 'Description' }
And then, I could do :
Reflection_GetSubfield(DS_Format : 'Label'); => return 'myLabel'
I wish i could do this too :
Reflection_GetSubfieldType(DS_Format : 'Label'); => return 'A'
Reflection_GetSubfieldLength(DS_Format : 'Label'); => return 50
Reflection_GetSubfieldPrecision(DS_Format : 'Type'); => return 0
With this, I expect I could do something like this (with some little work) :
SerializeXml(DS_Format); //I build xml with one line of code !
And get :
<DS_Format>
<Type>1</Type>
<Label>myLabel</Label>
<Description>myDescription</Description>
</DS_Format>
And conversely with DeserializeXml(myXml);
Reflection would help me to build really cool apis.
Is there any way ?

I have been contemplating some of these concepts, and may have a work around. (I don't have time at the moment to write a full answer & flesh out the details yet, but waited you to see there is some hope ;-) although some may consider it a cheat.)
The basic concept is this: IF you define a table with the desired format if your days structure, enabling the DS to be externally defined, then with embedded SQL you could DESCRIBE the table or query SYSCOLUMNS to get your field definitions, preferably in procedures.
Granted, this is not the same thing as refection, but could accomplish much the same. And one would probably only do this in limited circumstances. I'm sure others will point out a variety of issues, but the point here is that it is possible.

I'm wondering if the debug APIs could help get at least some of the behavior you are asking for...
http://pic.dhe.ibm.com/infocenter/iseries/v7r1m0/topic/apis/debug1.htm
One of the included functions is
Dump Module Variables (QteDumpModuleVariables) API
Of course, you're job would have to be in debug mode to use these APIs...

The following is a very simple implementation of WarrenT's idea.
create table ds_format
(
Type numeric(20, 0),
Label char(50),
Description char(5000)
);
Constant for the SQL description area:
D SQL_NUM C 99
Implementation of Reflection_ListSubfields in an SQLRPGLE module that returns an array of field names:
P Reflection_ListSubfields...
P B
*
D PI 80A Dim(SQL_NUM)
D name 30A Const
*
D tableName S Like(name)
D i S 3S 0 Inz
D fieldList S 80A Dim(SQL_NUM)
/free
EXEC SQL include SQLDA;
// retrieve description of the table
tableName = name;
EXEC SQL describe table :tableName into :sqlda;
// loop over all fields and
// retrieve the name
for i = 1 to SQLD;
SQLVAR = SQL_VAR(i);
fieldList(i) = SQLNAME;
endfor;
return fieldList;
/end-free
P E
Now a very rough implementation of Reflection_GetSubfield that returns the value as a string:
P Reflection_GetSubfield...
P B
*
D PI 32000A Varying
D dataStruct * Const
D name 30A Const
D fieldName 80A Const
*
D tableName S Like(name)
D i S 3S 0 Inz
D start S 6S 0 Inz(1)
D length S 6S 0 Inz(1)
D p_str S *
D str S 32000A Based(p_str)
D value S 32000A Varying
/free
EXEC SQL include SQLDA;
// retrieve description of the table
tableName = name;
EXEC SQL describe table :tableName into :sqlda;
// loop over all fields
for i = 1 to SQLD;
SQLVAR = SQL_VAR(i);
length = SQLLEN;
// Zoned decimal?
if SQLTYPE = 489;
length = SQLLEN / 256;
endif;
// field found?
if SQLNAME = fieldName;
leave;
endif;
start += length;
endfor;
p_str = dataStruct;
// retrieve value from our string
value = %trim(%subst(str: start: length));
return value;
/end-free
P E
I guess with this information it is relatively easy to implement the other three procedures.

Related

Java 7 => Java 8 : for to foreach on a Map with calculation

I begin with Java 8 and i have a migration project. I have read a lot of documentation and tutorial to use foreach or streams but i have a little last problem. I don't find the answer, just tutorial easy example.
I'm trying to transform this loop :
for ( Map.Entry<Neuron, Double> entry: this.entries.entrySet() ) {
value += entry.getKey().getExitValue() * entry.getValue();
}
This solution doesn't match and i know why (anonymous class => final/local var)
this.entries.forEach( (neuron, weight) -> {
value += neuron.getExitValue() * weight;
});
But only with a foreach i don't know how do this simple operation.
I think it's very easy but...
I have try with stream but i have similar problems.
Double sum = entries.entrySet()
.stream()
.forEach( entry-> { ? } );
Thanks you in advance.
As #Holger said in the comments above - in this case it is better to use mapToDoble. However there is still a way to do it using forEach loop. Please note that it is an ugly, dirty trick and it is just for demonstration purposes and it shouldn't be used in production code. As we know only final or effectively final variables can be used with lambda expressions, that's why value += is an illegal expression. Java-8 added a few new classes to java.util.concurrent.atomic one of them is DoubleAdder. You can use it with lambda:
DoubleAdder adder = new DoubleAdder();
stream.forEach(e -> adder.add(e.getKey().getExitValue() * e.getValue()));
System.out.println(adder.sum());
I don't see any cases when this should be used instead of mapToDouble
I introduced a list to stall the values and then do calculation with list.
final List<BigDecimal> valuesList = new ArrayList<>();
otherList.stream().forEach(val-> valuesList.add(map.get(val)));
final BigDecimal lastValue = valuesList.stream().filter(Objects::nonNull).reduce(BigDecimal.ZERO,BigDecimal::add);

Why is my query so slow?

I try to tune my query but I have no idea what I can change:
A screenshot of both tables: http://abload.de/image.php?img=1plkyg.jpg
The relation is: 1 UserPM (a Private Message) has 1 Sender (User, SenderID -> User.SenderID) and 1 Recipient (User, RecipientID -> User.UserID) and 1 User has X UserPMs as Recipient and X UserPMs as Sender.
The intial load takes around 200ms, it only takes the first 20 rows and display them. After this is displayed a JavaScript PageMethod gets the GetAllPMsAsReciepient method and loads the rest of the data
this GetAllPMsAsReciepient method takes around 4.5 to 5.0 seconds each time to run on around 250 rows
My code:
public static List<UserPM> GetAllPMsAsReciepient(Guid userID)
{
using (RPGDataContext dc = new RPGDataContext())
{
DateTime dt = DateTime.Now;
DataLoadOptions options = new DataLoadOptions();
//options.LoadWith<UserPM>(a => a.User);
options.LoadWith<UserPM>(a => a.User1);
dc.LoadOptions = options;
List<UserPM> pm = (
from a in dc.UserPMs
where a.RecieverID == userID
&& !a.IsDeletedRec
orderby a.Timestamp descending select a
).ToList();
TimeSpan ts = DateTime.Now - dt;
System.Diagnostics.Debug.WriteLine(ts.Seconds + "." + ts.Milliseconds);
return pm;
}
}
I have no idea how to tune this Query, I mean 250 PMs are nothing at all, on other inboxes on other websites I got around 5000 or something and it doesn't need a single second to load...
I try to set Indexes on Timestamp to reduce the Orderby time but nothing happend so far.
Any ideas here?
EDIT
I try to reproduce it on LinqPad:
Without the DataLoadOptions, in LinqPad the query needs 300ms, with DataLoadOptions around 1 Second.
So, that means:
I could save around 60% of the time, If I can avoid to load the User-table within this query, but how?
Why Linqpad needs only 1 second on the same connection, from the same computer, where my code is need 4.5-5.0 seconds?
Here is the execution plan: http://abload.de/image.php?img=54rjwq.jpg
Here is the SQL Linqpad gives me:
SELECT [t0].[PMID], [t0].[Text], [t0].[RecieverID], [t0].[SenderID], [t0].[Title], [t0].[Timestamp], [t0].[IsDeletedRec], [t0].[IsRead], [t0].[IsDeletedSender], [t0].[IsAnswered], [t1].[UserID], [t1].[Username], [t1].[Password], [t1].[Email], [t1].[RegisterDate], [t1].[LastLogin], [t1].[RegisterIP], [t1].[RefreshPing], [t1].[Admin], [t1].[IsDeleted], [t1].[DeletedFrom], [t1].[IsBanned], [t1].[BannedReason], [t1].[BannedFrom], [t1].[BannedAt], [t1].[NowPlay], [t1].[AcceptAGB], [t1].[AcceptRules], [t1].[MainProfile], [t1].[SetShowHTMLEditorInRPGPosts], [t1].[Age], [t1].[SetIsAgePublic], [t1].[City], [t1].[SetIsCityShown], [t1].[Verified], [t1].[Design], [t1].[SetRPGCountPublic], [t1].[SetLastLoginPublic], [t1].[SetRegisterDatePublic], [t1].[SetGBActive], [t1].[Gender], [t1].[IsGenderVisible], [t1].[OnlinelistHidden], [t1].[Birthday], [t1].[SetIsMenuHideable], [t1].[SetColorButtons], [t1].[SetIsAboutMePublic], [t1].[Name], [t1].[SetIsNamePublic], [t1].[ContactAnimexx], [t1].[ContactRPGLand], [t1].[ContactSkype], [t1].[ContactICQ], [t1].[ContactDeviantArt], [t1].[ContactFacebook], [t1].[ContactTwitter], [t1].[ContactTumblr], [t1].[IsContactAnimexxPublic], [t1].[IsContactRPGLandPublic], [t1].[IsContactSkypePublic], [t1].[IsContactICQPublic], [t1].[IsContactDeviantArtPublic], [t1].[IsContactFacebookPublic], [t1].[IsContactTwitterPublic], [t1].[IsContactTumblrPublic], [t1].[IsAdult], [t1].[IsShoutboxVisible], [t1].[Notification], [t1].[ShowTutorial], [t1].[MainProfilePreview], [t1].[SetSound], [t1].[EmailNotification], [t1].[UsernameOld], [t1].[UsernameChangeDate]
FROM [UserPM] AS [t0]
INNER JOIN [User] AS [t1] ON [t1].[UserID] = [t0].[RecieverID]
WHERE ([t0].[RecieverID] = #p0) AND (NOT ([t0].[IsDeletedRec] = 1))
ORDER BY [t0].[Timestamp] DESC
If you want to get rid of the LoadWith, you can select your field explicitly :
public static List<Tuple<UserPM, User> > GetAllPMsAsReciepient(Guid userID)
{
using (var dataContext = new RPGDataContext())
{
return (
from a in dataContext.UserPMs
where a.RecieverID == userID
&& !a.IsDeletedRec
orderby a.Timestamp descending
select Tuple.Create(a, a.User1)
).ToList();
}
}
I found a solution:
At first it seems that with the DataLoadOptions is something not okay, at second its not clever to load a table with 30 Coloumns when you only need 1.
To Solve this, I make a view which covers all nececeery fields and of course the join.
It reduces the time from 5.0 seconds to 230ms!

how to set value to 0 if sql query is invalid or finds no results?

I use the following code to prefil a field with an amount from a database.
$amount = db_result(db_query('SELECT amount FROM {table} WHERE nid = %d', $fid));
$node->edit_user_fid = $amount;
If the query doesnt find anything it just shows empty. How can I get the value to be 0 if theres no results?
$node->edit_user_fid = ($amount) ? $amount : 0;
Or also writeable as
if (!$amount) {
$node->edit_user_fid = 0;
}
or if you're sure that $amout is numerical (and it should be), use
$node->edit_user_fid = intval($node->edit_user_fid);
if amount is an int, you can use intval() or an (int) cast so you will always get a numeric value.
db_result() returns FALSE when it cannot find any table row matching the query being executed. Just cast the returned value to an integer.
$node->edit_user_fid = (int) db_result(db_query('SELECT amount FROM {table} WHERE nid = %d', $fid));
PHP manual
-.- you can use ===false or ===0 to teste if it's zero or false (test type and value)
no need to test false and reset to 0 or to set type of variable

Entity Framework - NotSupportedException

var depts = ctx.Departments
.OrderBy(d => d.deptName)
.Select(d => d.deptNo);
foreach (int deptNumber in depts) {
var deptReports = from d in ctx.Departments
join r in matchingIncidents on d.deptNo equals r.deptNo
where r.deptNo == deptNumber
select r;
int deptReportsCount = deptReports.Count();
I am completely baffled! All the questions about this error say to use == on primitive fields (such as IDs), which I'm doing. Anything I do to this query generates the exception. The exact same code worked before and I don't know what I've done to it! Could someone please explain to me what's going on?
Also, I remember there being an EntityFramework class with methods that allowed you to convert objects within a query (e.g. dates), does anyone know what this class is?
UPDATE:
Here are the changes I made (it now works).
var deptReports = from r in matchingIncidents
join d in ctx.Departments on r.deptNo equals d.deptNo
where r.deptNo == deptNumber
select r;
matchingIncidents looks like a local collection of a complex type (because you are using r.deptNo). That's not allowed in LINQ to Entities. You could try this instead:
foreach (int deptNumber in depts) {
var deptReports = from d in ctx.Departments
join r in matchingIncidents.Select(m => m.deptNo)
on d.deptNo equals r
where r == deptNumber
select r;
int deptReportsCount = deptReports.Count();
matchingIncidents.Select(m => m.deptNo) is now a local collection of primitive types and deptReports is a sequence of int (assuming that deptNo is of type int). But for counting the resulting elements it should be still fine.
Edit
And you are probably searching for the static EntityFunctions class:
http://msdn.microsoft.com/en-us/library/system.data.objects.entityfunctions.aspx
Could the issue be:
join r in matchingIncidents on d.deptNo == r.deptNo

FLEX XMLDecoder turns `09.00` to "09.00", but `10.00` to 10

Could someone explain why the FLEX 4.5 XMLDecoder does this to my XML-data?
var decoder:XMLDecoder = new XMLDecoder;
var $object:Object = decoder.decode( <xmltag>08.00</xmltag> );
// object = "08.00"
var decoder:XMLDecoder = new XMLDecoder;
var $object:Object = decoder.decode( <xmltag>11.00</xmltag> );
// Object = "11" (HEY! Where did my '.00' part of the string go?)
var decoder:XMLDecoder = new XMLDecoder;
var $object:Object = decoder.decode( <xmltag>11.30</xmltag> );
// Object = "11.3" (HEY! Where did my '0' part of the string go?)
The Flex deserializer also gave me issues with this. It may be interpreting them as Number objects and thus they will return short representations when toString() is called.
Try using .toFixed(2) whenever you need to print a value such as 11.00
var $object:Object = decoder.decode( <xmltag>11.00</xmltag> );
trace($object); //11
trace($object.toFixed(2)); //11.00
So, to the answer the original question of why this is happening:
In the source code for SimpleXMLDecoder (which I'm guessing has similar functionality to XMLDecoder), there's a comment in the function simpleType():
//return the value as a string, a boolean or a number.
//numbers that start with 0 are left as strings
//bForceObject removed since we'll take care of converting to a String or Number object later
numbers that start with 0 are left as strings - I guess they thought of phone numbers but not decimals.
Also, because of some hacky implicit casting, you actually have three different types -
"0.800" : String
11 : int
11.3: Number

Resources