Enum types (Enumerations) are very common to object oriented programming languages or frameworks – like .NET. Enums are typically used when parameters to functions or values of properties can only be a value from a list of values. For the purpose of this post we will look at an Enum for order statuses (Test.Enum.OrderStatusEnum). This Enum will have the following static values:
OrderStatusEnum:Ordered
OrderStatusEnum:Shipped
OrderStatusEnum:Invoiced
Enums simplify programming and thus reduce bugs because developers don’t have to remember numeric or character values for parameters that are critical to the program logic. Enums have many advantages over traditional solutions for this issue (i.e. preprocessor constants). Enums are type safe. That means that the compiler will only accept valid enum values when referencing the values as data members of the Enum type itself. Furthermore values from two different Enum types are typically not compatible to each other. This means that a parameter or property value expecting an OrderStatusEnum value will – validated by the compiler and the ABL runtime – not accept a value from a different Enum. Traditional solutions like preprocessor constants typically end with using CHARACTER or INTEGER values which typically don’t resolve the issue of typos (a misspelled preprocessor constant simply returns a blank string and does not lead to compiler message). Last but not least is the syntax completion of the OpenEdge Architect’s ABL editor very nicely able to support developers with completing class members in source code.
However the ABL with its object oriented capabilities does not provide a default Enum implementation and for instance no Enum base type.
Consultingwerk’s Enum pattern / implementation
We have implemented the following Enum pattern for the ABL successfully in our SmartComponent Library and WinKit products. A single Enum type is implemented using the protected members Label and Value resulting in an Enum value member being more or less a Label/Value pair – actually a triple because the member name (implemented as a static property) should be the same name as the Label.
The Enum base class Consultingwerk.Enum implements four instance members: The properties for Label and Value and two overrides to the Equals() and ToString() methods of the Progress.Lang.Object providing a method to compare to Enum values for equality and string serialization of Enums (useful for displaying them in MESSAGE statements or log files). The Enum base class is abstract as there is no need to directly create instances of this class (this makes OpenEdge 10.2B a requirement, but the pattern should work without the ABSTRACT keyword on 10.1C and 10.2A as well).
The actual OrderStatusEnum class inherits from the Consultingwerk.Enum class and will contain three static properties following this pattern:
DEFINE PUBLIC STATIC PROPERTY Ordered AS OrderStatusEnum NO-UNDO
GET:
IF NOT VALID-OBJECT (OrderStatusEnum:Ordered) THEN
OrderStatusEnum:Ordered = NEW OrderStatusEnum (1, "Ordered") .
RETURN OrderStatusEnum:Ordered .
END GET .
PRIVATE SET.
As you can see the Enum values will be referenced by read-only static properties. Whenever a static property representing an Enum value member is read the first time an instance of the OrderStatusEnum representing an Enum value is created. There should always only be a single instance of a certain Enum value.
The constructor of the OrderStatusEnum class assigns the value and the label to the properties defined in the base type.
/*------------------------------------------------------------------------------
Purpose: Privately accessible constructor, only accessible from static members
Notes:
------------------------------------------------------------------------------*/
CONSTRUCTOR PRIVATE OrderStatusEnum (piValue AS INTEGER, pcLabel AS CHARACTER):
SUPER ().
ASSIGN THIS-OBJECT:Value = piValue
THIS-OBJECT:Label = pcLabel .
END CONSTRUCTOR.
The constructor is private, so only (static) members of the OrderStatusEnum class will be able to create instances.
Serialization and Deserialization of Enum members
It may be required to serialize and deserialize Enum values. This is typically done as CHARACTER values for better human readability. The override to Progress.Lang.Object:ToString() in Consultingwerk.Enum does already provide serialization functionality of the Enum value as CHARACTER. When you need to deserialize a CHARACTER value retrieved using this function, you can implement a static FromString() function in the OrderStatusEnum:
/*------------------------------------------------------------------------------
Purpose: Deserializes a CHARACTER value into a reference to an Enum member
Notes:
------------------------------------------------------------------------------*/
METHOD PUBLIC STATIC OrderStatusEnum FromString (pcCharacterValue AS CHARACTER):
CASE pcCharacterValue:
WHEN "Ordered" THEN
RETURN OrderStatusEnum:Ordered .
WHEN "Shipped" THEN
RETURN OrderStatusEnum:Shipped .
WHEN "Invoiced" THEN
RETURN OrderStatusEnum:Invoiced .
OTHERWISE
UNDO, THROW NEW AppError (SUBSTITUTE ("Invalid Enum value &1", pcCharacterValue), 0) .
END CASE .
END METHOD.
Sample Source code
Sample source code can be downloaded here.
When using our Enum pattern in any form, please be aware that the software is distributed “as is”, without warranty of any kind, either express or implied! You may modify the software as needed, however we kindly ask you to mention the original author of the software or the link to this blog.
Share