Scaffolding utility for common utility classes
Introduction
OO programming frequently leverages several helper classes such as Enum's, Parameter objects and Exceptions. Developers tend to use those beneficial patterns more frequently when creation of those helping objects is as simple as possible. Due to limitations of different OpenEdge releases a few of those patterns lead to a large amount of repeatedly written code.
We therefore provide a common line interface based (CLI) scaffolding for
- Enums
- Serializable Parameter objects
- Exceptions
- Generic Lists and Dictionaries
- Strong-typed Named Queries
- Value Object Cache
Quick Reference
scl-gen Enum SampleEnum Monday Tuesday Wednesday Thursday Friday Saturday Sunday scl-gen Parameter SampleParameter Value1 Value2 scl-gen ParameterWithInterface SampleParameter Value1 Value2 scl-gen ParameterWithReadOnlyInterface SampleParameter Value1 Value2 scl-gen Interface SampleInterface Value1 Value2 scl-gen ReadOnlyInterface SampleInterface Value1 Value2 scl-gen ValueObject Date Day:integer Month:integer Year:integer scl-gen Exception SampleException scl-gen GenericList ListSample MemberClassName scl-gen GenericDictionary DictionarySample MemberClassName scl-gen NamedQuery OrdersInMonthQuery OrdersInMonth Month Year
Property data-types
When scl-gen command line parameters resemble properties in the generated class, those will be generated by default as CHARACTER. That is not always what you need so you can specify the data-type after the colon per property.
scl-gen Parameter SampleParameter Value1:Integer Value2:int Value3:dec Value4:log Value5
Requirements
The scaffolding utility is implemented using the scl-gen batch script. This batch script is located in the Consultingwerk/Studio/Scaffolding folder of the SmartComponent Library and should be added to the Windows PATH.
The scaffolding utility is further leveraging Apache ANT and PCT. We expect that an ANT installation with PCT.jar in the lib folder is accessible using the Windows PATH as well.
The scaffolding utility needs to make certain assumptions about the development environment. The utility can only be used on the command line from within in a sub folder of a Progress Developer Studio (PDSOE) project. The utility will search the directory tree upwards from the current directory and will fail if no .propath utility (default PDSOE configuration) is found. The .propath file is required to gather information about the Progress Developer Studio project structure.
The utility is partly implemented in ABL - so a Progress runtime must be located. When no DLC environment variable is set, the utility will seek for a supported Progress version in the Windows registry.
Scaffolding
The scaffolding is generally executed using the following command. The parameters for the Template Name and the Class Name are mandatory. The generated classes Package name is determined automatically based on the working directory relatively to the PDSOE's projects's root folder:
scl-gen <Template Name> <Class Name> <Parameter 1> <Parameter 2> <Parameter 3> ....
Enums
The following scaffolding command creates an Enum which can be used as an ABL native Enum (OpenEdge 11.6 ...) or a Consultingwerk Enum (prior to 11.6).
scl-gen Enum SampleEnum Monday Tuesday Wednesday Thursday Friday Saturday Sunday
This creates the class file SampleEnum.cls in the current directory:
/*------------------------------------------------------------------------ File : SampleEnum Purpose : Syntax : Description : Author(s) : Created : 06.09.2017 09:52:47 Notes : ----------------------------------------------------------------------*/ {Consultingwerk/products.i} &IF DEFINED (NativeEnums) NE 0 &THEN ENUM Test.SampleEnum: DEFINE ENUM Monday Tuesday Wednesday Thursday Friday Saturday Sunday . END ENUM . &ELSE BLOCK-LEVEL ON ERROR UNDO, THROW. USING Test.* FROM PROPATH . USING Progress.Lang.* FROM PROPATH . CLASS Test.SampleEnum INHERITS Consultingwerk.Enum: {Consultingwerk/EnumMember.i Monday 1 SampleEnum} {Consultingwerk/EnumMember.i Tuesday 2 SampleEnum} {Consultingwerk/EnumMember.i Wednesday 3 SampleEnum} {Consultingwerk/EnumMember.i Thursday 4 SampleEnum} {Consultingwerk/EnumMember.i Friday 5 SampleEnum} {Consultingwerk/EnumMember.i Saturday 6 SampleEnum} {Consultingwerk/EnumMember.i Sunday 7 SampleEnum} /*------------------------------------------------------------------------------ Purpose: Constructor for the SampleEnum class Notes: @param piValue The integer enum value @param pcLabel The character enum label ------------------------------------------------------------------------------*/ CONSTRUCTOR PRIVATE SampleEnum (piValue AS INTEGER, pcLabel AS CHARACTER): SUPER (). ASSIGN THIS-OBJECT:Value = piValue THIS-OBJECT:Label = pcLabel . END CONSTRUCTOR. {Consultingwerk/EnumFromString.i Test.SampleEnum} END CLASS. &ENDIF
Serializable Parameter Objects
The following scaffolding command creates an JSON serializable Parameter object .
scl-gen Parameter SampleParameter Value1 Value2
This creates the class file SampleParameter.cls in the current directory:
/*------------------------------------------------------------------------ File : SampleParameter Purpose : Syntax : Description : Author(s) : Created : 06.09.2017 09:56:27 Notes : ----------------------------------------------------------------------*/ BLOCK-LEVEL ON ERROR UNDO, THROW. {Consultingwerk/products.i} USING Consultingwerk.* FROM PROPATH . USING Test.* FROM PROPATH . USING Progress.Lang.* FROM PROPATH . CLASS Test.SampleParameter INHERITS JsonSerializable {&SERIALIZABLE}: {Consultingwerk/JsonSerializableProperty.i Value1 CHARACTER} . {Consultingwerk/JsonSerializableProperty.i Value2 CHARACTER} . /*------------------------------------------------------------------------------ Purpose: Constructor for the SampleParameter class Notes: ------------------------------------------------------------------------------*/ CONSTRUCTOR PUBLIC SampleParameter (): SUPER (). END CONSTRUCTOR. /*------------------------------------------------------------------------------ Purpose: Constructor for the SampleParameter class Notes: @param pcValue1 The value for the Value1 property @param pcValue2 The value for the Value2 property ------------------------------------------------------------------------------*/ CONSTRUCTOR PUBLIC SampleParameter (pcValue1 AS CHARACTER, pcValue2 AS CHARACTER): SUPER (). ASSIGN THIS-OBJECT:Value1 = pcValue1 THIS-OBJECT:Value2 = pcValue2 . END CONSTRUCTOR. END CLASS.
Exceptions
The following scaffolding command creates an Exception object .
scl-gen Exception SampleException
This creates the class file SampleException.cls in the current directory:
/*------------------------------------------------------------------------ File : SampleException Purpose : Syntax : Description : Author(s) : Created : 06.09.2017 09:58:17 Notes : ----------------------------------------------------------------------*/ BLOCK-LEVEL ON ERROR UNDO, THROW . {Consultingwerk/products.i} USING Consultingwerk.* FROM PROPATH . USING Consultingwerk.Exceptions.* FROM PROPATH . USING Test.* FROM PROPATH . USING Progress.Lang.* FROM PROPATH . CLASS Test.SampleException INHERITS Exception {&SERIALIZABLE}: /** * Purpose: Constructor of the SampleException class * Notes: */ CONSTRUCTOR PUBLIC SampleException (): SUPER (). END CONSTRUCTOR. /** * Purpose: Constructor of the SampleException class * Notes: * @param pcErrorString The error message associated with this Exception object * @param piMessageNumber The error message number associated with this Exception object */ CONSTRUCTOR PUBLIC SampleException (pcErrorString AS CHARACTER, piMessageNumber AS INTEGER): SUPER (pcErrorString, piMessageNumber). END CONSTRUCTOR. /** * Purpose: Constructor of the SampleException class * Notes: * @param pcErrorString The error message associated with this Exception object */ CONSTRUCTOR PUBLIC SampleException (pcErrorString AS CHARACTER): SUPER (pcErrorString). END CONSTRUCTOR. /** * Purpose: Constructor of the SampleException class * Notes: * @param poInnerException The reference to the original error */ CONSTRUCTOR PUBLIC SampleException (poInnerException AS Error): SUPER (poInnerException). END CONSTRUCTOR. /** * Purpose: Constructor of the SampleException class * Notes: * @param poInnerException The reference to the original error * @param pcErrorString The error message associated with this Exception object * @param piMessageNumber The error message number associated with this Exception object */ CONSTRUCTOR PUBLIC SampleException (poInnerException AS Error, pcErrorString AS CHARACTER, piMessageNumber AS INTEGER): SUPER (poInnerException, pcErrorString, piMessageNumber). END CONSTRUCTOR. /** * Purpose: Constructor of the SampleException class * Notes: * @param poInnerException The reference to the original error * @param pcErrorString The error message associated with this Exception object */ CONSTRUCTOR PUBLIC SampleException (poInnerException AS Error, pcErrorString AS CHARACTER): SUPER (poInnerException, pcErrorString). END CONSTRUCTOR. END CLASS.
GenericList
The following scaffolding command creates an GenericList object for the given MemberClassName. The Add method is provided as a sample for custom add methods. This can be changed to fit or removed.
scl-gen GenericList ListSample MemberClassName
This creates the class file SampleException.cls in the current directory:
/*------------------------------------------------------------------------ File : ListSample Purpose : Syntax : Description : Author(s) : Created : 08.09.2017 06:36:37 Notes : ----------------------------------------------------------------------*/ BLOCK-LEVEL ON ERROR UNDO, THROW. USING Consultingwerk.Framework.Base.* FROM PROPATH . USING Test.* FROM PROPATH . USING Progress.Lang.* FROM PROPATH . {Consultingwerk/products.i} CLASS Test.ListSample INHERITS GenericList: {Consultingwerk/Framework/Base/GenericList.i MemberClassName} /* /*------------------------------------------------------------------------------ Purpose: Adds an Item to this List Notes: @param pcValue1 @param pcValue2 @param pcValue3 @return The Item that was created ------------------------------------------------------------------------------*/ METHOD PUBLIC MemberClassName Add (pcValue1 AS CHARACTER, pcValue2 AS CHARACTER, pcValue3 AS CHARACTER): DEFINE VARIABLE oItem AS Column NO-UNDO . oColumn = NEW Column () . ASSIGN oItem:Value1 = pcValue1 oItem:Value2 = pcValue2 oItem:Value3 = pcValue3 . RETURN THIS-OBJECT:Add (oItem) . END METHOD . */ END CLASS.
GenericDictionary
The following scaffolding command creates an GenericList object for the given MemberClassName.
scl-gen GenericDictionary DictionarySample MemberClassName
This creates the class file SampleException.cls in the current directory:
/*------------------------------------------------------------------------ File : DictionarySample Purpose : Syntax : Description : Author(s) : Created : 08.09.2017 06:43:15 Notes : ----------------------------------------------------------------------*/ BLOCK-LEVEL ON ERROR UNDO, THROW. USING Consultingwerk.Framework.Base.* FROM PROPATH . USING Test.* FROM PROPATH . USING Progress.Lang.* FROM PROPATH . {Consultingwerk/products.i} CLASS Test.DictionarySample INHERITS GenericDictionary: {Consultingwerk/Framework/Base/GenericDictionary.i MemberClassName} END CLASS.
Strong-typed Named Queries
The following command creates a strong-typed Named Query Parameter class.
scl-gen NamedQuery OrdersInMonthQuery OrdersInMonth Month Year
This creates the class file OrdersInMonthQuery.cls in the current directory:
/*------------------------------------------------------------------------ File : OrdersInMonthQuery Purpose : Syntax : Description : Author(s) : Created : 27.09.2017 21:29:10 Notes : ----------------------------------------------------------------------*/ USING Consultingwerk.OERA.* FROM PROPATH . {Consultingwerk/products.i} CLASS Test.OrdersInMonthQuery INHERITS NamedQueryParameter: /*------------------------------------------------------------------------------ Purpose: Constructor for the OrdersInMonthQuery class Notes: @param pcMonth The Month query parameter @param pcYear The Year query parameter ------------------------------------------------------------------------------*/ CONSTRUCTOR PUBLIC OrdersInMonthQuery (pcMonth AS CHARACTER, pcYear AS CHARACTER): SUPER ("OrdersInMonth":U). THIS-OBJECT:Parameters:Add ("Month":U, pcMonth) . THIS-OBJECT:Parameters:Add ("Year":U, pcYear) . END CONSTRUCTOR. END CLASS.
Value Object
The following scaffolding command creates an immutable value object.
scl-gen ValueObject WeightValueObject Weight:decimal Unit
This creates the class file WeightValueObject.cls in the current directory:
/*------------------------------------------------------------------------ File : WeightValueObject Purpose : Syntax : Description : Author(s) : Created : 28.05.2019 06:53:57 Notes : ----------------------------------------------------------------------*/ BLOCK-LEVEL ON ERROR UNDO, THROW. {Consultingwerk/products.i} USING Consultingwerk.* FROM PROPATH . USING Test.* FROM PROPATH . USING Progress.Lang.* FROM PROPATH . CLASS Test.WeightValueObject INHERITS ValueObject: /** * Purpose: Returns the Weight value * Notes: */ DEFINE PUBLIC PROPERTY Weight AS DECIMAL NO-UNDO GET. PRIVATE SET. /** * Purpose: Returns the Unit value * Notes: */ DEFINE PUBLIC PROPERTY Unit AS CHARACTER NO-UNDO GET. PRIVATE SET. /** * Purpose: Constructor for the WeightValueObject class * Notes: * @param pWeight The value for the Weight property * @param pUnit The value for the Unit property */ CONSTRUCTOR PUBLIC WeightValueObject (pWeight AS DECIMAL, pUnit AS CHARACTER): ASSIGN THIS-OBJECT:Weight = pWeight THIS-OBJECT:Unit = pUnit . END CONSTRUCTOR. END CLASS.
Value Object Cache
The following scaffolding command creates a cache for value objects.
scl-gen ValueObjectCache CharacterHolderCache Consultingwerk.CharacterHolder
This creates the class file CharacterHolderCache.cls in the current directory:
/*------------------------------------------------------------------------ File : CharacterHolderCache Purpose : Syntax : Description : Author(s) : Created : 27.12.2018 23:58:01 Notes : ----------------------------------------------------------------------*/ BLOCK-LEVEL ON ERROR UNDO, THROW. USING Consultingwerk.Framework.Base.* FROM PROPATH . USING Test.* FROM PROPATH . USING Progress.Lang.* FROM PROPATH . {Consultingwerk/products.i} CLASS Test.CharacterHolderCache INHERITS ValueObjectCacheBase: {Consultingwerk/Framework/Base/GenericValueObjectCache.i CharacterHolderCache Consultingwerk.CharacterHolder} END CLASS.
Accessing the command line interface (CLI) from within Progress Developer Studio
This discussion on Stack Overflow describes a couple of approaches which allow a developer to access CLI tools from within Progress Developer Studio. Here's our preferred one.
Create an External Tools Configuration with the followinig settings:
Setting | Value |
---|---|
Name | CLI Console |
Location | ${env_var:ComSpec} |
Working Directory | ${resource_loc} |
Allocate Console | Checked |
Launch in Background | Checked |
This configuration should be used in the following way:
- Select a folder in the "Project Explorer" view
- Execute the "CLI Console" custom tool configuration
This will launch a console window with the selected folder as the working directory.
Now you can execute the scl-gen tools from the Console view: