Feature #9437
enhancements for Progress.Lang.Class
100%
Related issues
History
#1 Updated by Greg Shah over 1 year ago
Progress.Lang.Class.haswidgetpool() - unsupported Progress.Lang.Class.invoke - this can be considered 'partial' now, it needs proper testing (maybe there are tests written by Marian...) Progress.Lang.Object.clone() - this should work, but I don't recall seeing it in a current customer project reflection usage: * Progress.Reflect.Property (the entire package actually...) * Progress.Lang.Class.getproperties() * Progress.Lang.Class.getproperty(KW_INPUT character)
#2 Updated by Alexandru Lungu over 1 year ago
- Assignee set to Stefan Vieru
#3 Updated by Constantin Asofiei about 1 year ago
- Assignee changed from Stefan Vieru to Eduard Soltan
#4 Updated by Eduard Soltan about 1 year ago
Greg Shah wrote:
Progress.Lang.Class.haswidgetpool() - unsupported
Please observe my test class:
class oo.MyClass use-widget-pool: end.
use-widget-pool will be emitted in fwd as WidgetPool.create() in _execute__ method:
@LegacySignature(type = Type.EXECUTE)
public void __oo_MyClass_execute__()
{
WidgetPool.create();
{
}
}
In this way we will not know that the class has a WidgetPool until we actually instantiate an object.
However it seems that we could use reflection in 4GL to see is the class has use-widget-pool or not.
def var obj as oo.MyClass.
DEFINE VARIABLE myType AS Progress.Lang.Class NO-UNDO.
myType = Progress.Lang.Class:GetClass("oo.MyClass").
message myType:HasWidgetPool(). // YES
I think we could emit in converted class a field of type WidgetPoolData.
#5 Updated by Constantin Asofiei about 1 year ago
Eduard Soltan wrote:
I think we could emit in converted class a field of type
WidgetPoolData.
Please emit an annotation at the converted MyClass.java file.
#6 Updated by Eduard Soltan about 1 year ago
Added runtime support for Progress.Lang.Class.haswidgetpool(), in 9437a, rev. 15908.
#7 Updated by Eduard Soltan about 1 year ago
- % Done changed from 0 to 20
- Status changed from New to WIP
#8 Updated by Eduard Soltan about 1 year ago
Greg Shah wrote:
Progress.Lang.Class.invoke - this can be considered 'partial' now, it needs proper testing (maybe there are tests written by Marian...)
Marian, are there any testcases written for this feature?
#9 Updated by Marian Edu about 1 year ago
Eduard Soltan wrote:
Greg Shah wrote:
Progress.Lang.Class.invoke - this can be considered 'partial' now, it needs proper testing (maybe there are tests written by Marian...)
Marian, are there any testcases written for this feature?
All test cases for OO ABL are inside `tests/oo` folder, for this particular case the location will be `tests/oo/progress/lang/class/invoke`.
#10 Updated by Eduard Soltan about 1 year ago
class oo.NoWidgetPoolClass inherits oo.SecondClass: DEFINE PROTECTED PROPERTY A2 AS CHARACTER NO-UNDO PRIVATE GET. SET. end.
We can specify the access mode for the property to [PUBLIC | PROTECTED | PRIVATE].
For PROPERTY we can also specify a GET and SET. For getters and setters we can also specify the access mode. But there some rules to it:
1) if the access mode for GET or SET is not specified, then it defaults the access mode of the property.
2) we can only specify the access mode to one of the methods (GET or SET). If we change the default access mode for GET, we can't change it for SET.
3) we can only specify an access mode that is more restrictive then the property one. If access mode of the PROPERTY is PROTECTED, we could only specify PRIVATE.
The converted code will look like this:
public class NoWidgetPoolClass
extends com.goldencode.dataset.oo.SecondClass
{
@LegacySignature(type = Type.PROPERTY, name = "A2")
private character a2 = TypeFactory.character();
@LegacySignature(type = Type.GETTER, name = "A2", returns = "CHARACTER")
private character getA2()
{
return a2.duplicate();
}
@LegacySignature(type = Type.SETTER, name = "A2", parameters =
{
@LegacyParameter(name = "var", type = "CHARACTER", mode = "INPUT")
})
protected void setA2(character var)
{
a2.assign(var);
}
}
Problem is that in FWD the access mode of the converted field will always be private. But we could infer the access mode of the Property, from the getter and setter method. The least restrictive access mode of these methods will be the the access mode of the property.
#11 Updated by Constantin Asofiei about 1 year ago
Eduard Soltan wrote:
Problem is that in FWD the access mode of the converted field will always be private. But we could infer the access mode of the Property, from the
getterandsettermethod. The least restrictive access mode of these methods will be the the access mode of the property.
Nice find, I agree.
#12 Updated by Eduard Soltan about 1 year ago
Committed on 9437a, rev. 15910.
Committed implementation of Progress.Lang.Class:getVariables, Progress.Lang.Class:getVariable, Progress.Lang.Class:getProperties and Progress.LangClass:getProperty.
Implemented support for Progress.Reflect.Property with limited support for get and set.
Implemented support for Progress.Reflect.Variable with limited support for get and set.
#13 Updated by Eduard Soltan about 1 year ago
Marian,
For any methods from Progress.Lang.Class there is an overload with a parameter of type Progress.Reflect.Flags. The description from the docs is pretty straight forward, it select only those (variables / properties / methods) that match the Progress.Reflect.Flags enum.
However I am struggling a bit to match this behaviour in 4gl.
Please the following example:
start.p
def var cClass as Progress.Lang.Class.
def var pProperty as Progress.Reflect.Property.
def var pVariable as Progress.Reflect.Variable.
def var pMethod as Progress.Reflect.Method.
def var obj as oo.SecondClass.
obj = new oo.SecondClass().
cClass = Progress.Lang.Class:getClass("oo.SecondClass").
pVariable = cClass:GetVariable('B2', Progress.Reflect.Flags:PRIVATE).
message pVariable.
SecondClass.cls
class oo.SecondClass use-widget-pool: DEFINE public PROPERTY b1 AS CHARACTER extent NO-UNDO GET. SET. DEFINE private variable B2 AS oo.NoWidgetPoolClass NO-UNDO. DEFINE PRIVATE PROPERTY A2 AS CHARACTER NO-UNDO GET. SET. end.
pVariable will be UNKNOWN. Maybe I am doing something wrong.
#14 Updated by Constantin Asofiei about 1 year ago
Eduard, please rebase.
#15 Updated by Eduard Soltan about 1 year ago
Sure, but I am still working on some adjustments on Varibale.get ad set methods.
#16 Updated by Eduard Soltan about 1 year ago
- % Done changed from 20 to 50
Rebased to latest trunk + plus implementation adjustments in setter of Variable and setting of dataType.
#17 Updated by Eduard Soltan about 1 year ago
Progress.Reflect.Variable¶
AccessMode property: this could be inferred from the access mode of the java field using reflection. Access mode in 4gl code and in converted fwd code will coincide.
DataTypeName property and DataTypeName property:
I am not sure if data type of java field could be used to populate this information.
Lets take an example: def var public b2 as oo.MyClass will convert into public object extends com.goldencode.dataset.oo.MyClass> b2. Without any information about propath I could not deduce from just java field type the original 4Gl name of the class.
Also if the VARIABLE is of BaseDataType this field should be UNKNOWN.
My solution is to use a similar approach with how the return type is handled in Methods. Added a new property in LegacySignature (data_type) that will hold information about 4gl data type (integer, character, logical and object for Classes). In case the variable is of type object, it will have an addition property qualified with the original name of the Class.
Extent property: we could use the extent property of the LegacySignature. We should return UNKNOWN in case of dynamic extent (LegacySignature.extent == -1).
isStatic property: used reflection of the Java field.
OriginatingClass property and DeclaringClass property:
- for DeclaringClass reflection on the java field was used.
- for OriginatingClass, the class on which the Reflecion is preformed in 4gl is send as parameter in Variable constructor.
Progress.Reflect.Property¶
Things are bit different in 4GL and FWD. Even if the PROPERTY is declared as public in 4gl, it will convert in fwd as a private field. Because of this we can not infer the access mode, and frankly any other information just from Java field.
Besides the field, in FWD there are also getter and setter method that are generated. And the access modes of the Java methods match the access mode 4GL property.
According to the 4GL docs, at least one of the methods should match the access mode of the property. And the other one should have an access mode that is more restrictive then the one of the property.
So I could use the the Java method and its LegacySignature with the least restrictive access mode to infer information about the 4Gl property.
DataTypeName property and DataTypeName property: are inferred from LegacySignature of the method (in case of GETTER) and from the LegacySignature of the method parameter (in case of the SETTER).
Edge cases.¶
Property is defined abstract in 4GL. In this case the in FWD the field is not generated, and only the getter and setter methods are generated. In this case we should also look in the methods with LegacySignature of type GETTER or SETTER.
For a property of type EXTENT, multiple GETTERs and SETTERs methods are generated. For inference was used only the methods that have a array return type (GETTER) and an array type as parameter (SETTER).
#18 Updated by Eduard Soltan about 1 year ago
Set and Get methods¶
Progress.Reflect.Property inherits Progress.Reflect.Variable, so we should implement Set and Get method of Variable class in Property.
According to the 4GL docs Progress.Reflect.Property,
- Variable:Get when applied to a property, the method will invoke the property's GET method, if there is one.
- Variable:Set When applied to a property, the method will invoke the property's SET method, if there is one.
In FWD the implementation is to invoke the generated getters and setters methods.
Variable getter and setter¶
We could use assign of BaseDataType to set the value of the field and Java reflection to get the value of the field.
#19 Updated by Eduard Soltan about 1 year ago
I got the following example:
def var cClass as Progress.Lang.Class.
def var pProperty as Progress.Reflect.Property.
def var pVariable as Progress.Reflect.Variable.
def var obj as oo.SecondClass.
obj = new oo.SecondClass().
cClass = Progress.Lang.Class:getClass("oo.SecondClass").
pProperty = cClass:GetProperty("B4").
pVariable = cClass:GetVariable("B2").
pVariable:Set(obj, pProperty).
Where B2 is a variable of the Integer and B4 is a property of type Integer. And in my example I want to assign to B2 variable a instance of type Progress.Reflect.Property.
In my implementation it will throw and error. Please look at the following stack trace:
ObjectOps.asResource(ObjectOps$WorkArea, _BaseObject_) line: 3167 ObjectOps.asResource(_BaseObject_) line: 3152 object<T>.resourceId(object<_BaseObject_>) line: 233 integer(NumberType).assign(object<?>) line: 462 integer(int64).assign(BaseDataType) line: 1026 Call.setterAssignment(Object, Object, String, boolean) line: 2692
In ObjectOps.asResource it will try to get an ObjectResource from objects Map, but it will not find any resource of type Progress.Reflect.Property.
ObjectOps.asResource will be called only because ObjectOps.isValid(obj.ref) returns true for object obj.ref of type Progress.Reflect.Property.
ObjectOps.isValid will return true because isTracked for Progress.Reflect.Variable will return false.
public static boolean isValid(_BaseObject_ ref)
{
return ref != null && (!ref.isTracked() || ref.__isValidInternal__());
}
My idea was to add the register in ObjectOps the variables of Progress.Reflect.Property and Progress.Reflect.Varibale
#20 Updated by Constantin Asofiei about 1 year ago
I think if (ref instanceof LegacyEnum) needs to be extended to if (ref instanceof LegacyEnum || !ref.isTracked()).
#21 Updated by Constantin Asofiei about 1 year ago
base_structure- replaceWidgetPoolAnnotationwithLegacyWidgetPoolvariable_definitions,ParameterList- missing history entry- please align this code:
<rule>cls.startsWith("jobject") <action>cls = "jobject"</action> </rule>
- please align this code:
LegacyClass- the import is wrong:
org.apache.commons.collections4.map.CaseInsensitiveMap - missing javadoc for fields
- use
com.goldencode.util.CaseInsensitiveHashMapfor the fields hasWidgetPool- cache this value in aBooleanfield- if there is no
BlockDefinitiontop-level block, do not use TypeFactory to define vars. Check for other cases in your implementation/changes.
- the import is wrong:
- check for other cases of missing history entry and/or javadocs
#22 Updated by Eduard Soltan about 1 year ago
Fixed review issues in rev. 15966.
#23 Updated by Constantin Asofiei about 1 year ago
Eduard Soltan wrote:
Fixed review issues in rev. 15966.
I don't see this commit.
#24 Updated by Eduard Soltan about 1 year ago
Constantin Asofiei wrote:
Eduard Soltan wrote:
Fixed review issues in rev. 15966.
I don't see this commit.
Sorry forgot to bind the branch, now it can be seen.
#25 Updated by Constantin Asofiei about 1 year ago
LegacySignature.javaandvariable_definitions.rules- renamedata_typetodataTypeParameterList.java- please fix the entire class to removeBlockDefinitionusage, andlogicalconstantsObjectOps.java- history entry message is unrelated to the changeCall.java- eachErrorManager.recordOr...must be followed by a Java return statementLegacyClass- use
logical.ofand such constants getMethods- callclassMethods.putif the method is not yet cached- don't use
ArrayAssigner, just assign the index in the array, ingetProperties,getVariablesandgetMethods setPropertyValue-prop.ref()will raise an ERROR condition ifpropis unknown/invalid. Is this by intent? I see you already managedprop == null, so is not clear- do not use
UndoableFactory.<type>ifBlockManageris not used _getVariables- why not use aLinkedHashSetif you need the order?_getVariable-!lsig.name().equals(name.getValue())- this is done case-sensitive, which is wrongclassProperties, @classMethodsandclassVariablesneed to beCaseInsensitiveHashMap
- use
Property.java- please organize the imports to use 'star'
Variable.javaget()is not implemented? Or this is for extent vars?- please organize the imports to use 'star'
- double-check copyright year to be set to 2025
- in all changed files, add
UnimplementedFeature.missingto the stub methods, so we know what's left to implement. Variableand maybePropertyand others too - please double-check the defined fields in the class to not be assigned via Java '=', and instead always use.assign. All class fields fromVariablehave this problem.
Lets fix the review and document what's left to be implemented related reflection and progress.lang.class.
#26 Updated by Eduard Soltan about 1 year ago
Constantin Asofiei wrote:
Review for 9437a rev 15978:
_getVariables- why not use aLinkedHashSetif you need the order?
Do you mean I should get the variables in lexicographical order? Because in 4GL I get them in the order of definition.
#27 Updated by Constantin Asofiei about 1 year ago
Eduard Soltan wrote:
Constantin Asofiei wrote:
Review for 9437a rev 15978:
_getVariables- why not use aLinkedHashSetif you need the order?Do you mean I should get the variables in lexicographical order? Because in 4GL I get them in the order of definition.
That's why I mentioned LinkedHashSet . The point is I wanted to see if we can do something smarter than an 'ArrayList.contains'. Also, why not cache the result of _getProperties or _getVariables()? If caching is not used/possible, then I'd like to make it more efficient.
#28 Updated by Eduard Soltan about 1 year ago
Committed review issues and improvements for retrieval the variables and properties from the super class on rev. 15968.
setPropertyValue - prop.ref() will raise an ERROR condition if prop is unknown/invalid. Is this by intent? I see you already managed prop == null, so is not clear
Yes, if property-name is not a valid name of the property it will raise this condition in 4GL.
That's why I mentioned LinkedHashSet . The point is I wanted to see if we can do something smarter than an 'ArrayList.contains'. Also, why not cache the result of _getProperties or _getVariables()? If caching is not used/possible, then I'd like to make it more efficient.
I agree, but I think it will only be helpful when we will check for a super class property in the properties array.
#29 Updated by Constantin Asofiei about 1 year ago
- Status changed from WIP to Internal Test
Please put 9437a into test.
#30 Updated by Eduard Soltan about 1 year ago
Also modified the LegacyResourceSupport annotation.
Testing performed with 9437a:
- unit test and smoke tests of a large GUI application. ✅
- smoke tests of GUI application. ✅
- harness of a customer application. ✅
- conversion of regression tests project. ✅
#31 Updated by Constantin Asofiei about 1 year ago
- Status changed from Internal Test to Merge Pending
Can be merged after 9879a
#32 Updated by Eduard Soltan about 1 year ago
9437a was merged to trunk rev. 15967 and archived.
#33 Updated by Eduard Soltan about 1 year ago
- Status changed from Merge Pending to WIP
- % Done changed from 50 to 80
What is left to implement on this task:
- Progress.Reflect.Constructor
- Progress.Reflect.Event.
- setters and getters for properties and variables of type extent.
#34 Updated by Constantin Asofiei about 1 year ago
Eduard, please create a separate task in Base Language project for what is left in #9437-33.
#35 Updated by Eduard Soltan about 1 year ago
- Related to Bug #10314: Implementation of Progress.Lang.Class added
#36 Updated by Constantin Asofiei 12 months ago
- Status changed from WIP to Closed
- % Done changed from 80 to 100