Parsing Object Oriented Classes and References

Starting in OpenEdge v10, it became possible to write 4GL code structured as objects. This object oriented approach provides subset of OO support in traditional OO languages, including the ability to define a class and create instances (individual objects) of those classes. When creating new 4GL OO code, that code must be put into .cls source files using new OO syntax for defining classes, methods, properties and so forth.

These new classes will exist in the source tree of your project, along with the traditional procedural 4GL code, but the OO code will be organized into packages of hierarchical directories. Any 4GL code that references these classes will either use a fully qualified (package + classname) name or will use the non-qualified class name. Either way, when such references are encountered, the parsing of the referencing source file is paused while an attempt is made to find the class. In v11 partially qualified names can be referenced, but this is not supported by FWD yet. If the referenced class cannot be found, then the parsing of the current file will fail. Conversely, if the class is found, then that class is parsed and any referenced classes are searched for and parsed until the entire graph of referenced classes has been loaded. At that point the parsing of the original source file can continue.

Considering that the code is integrated into the rest of the project sources, the Progress approach was to search through the PROPATH to find fully qualified matches OR to check if an unqualified class name matched the class name portion of an explicitly specified USING statement OR to substitute the unqualified name into any USING specifications that have a wildcard and search the PROPATH for a match.

Most of the 4GL language still can only be accessed using existing non-object syntax. However, there is a small amount of functionality which has been created in built-in 4GL OO classes (e.g. Progress.Lang.Object) that can be accessed and subclassed just like other classes in the application. When classes need to be found, these built-in classes are also searched. The difference is that these classes don't reside in the application directories or PROPATH.

Starting in OpenEdge v11, this same OO syntax can be used to access .NET objects. Those .NET objects can be in .NET assembies or can be from the built-in .NET classes. Searches for classes must handle these additional possible matches.

It is this class resolution or searching process that needs special attention and configuration in order to properly parse OO 4GL code. This chapter provides guidance on how to configure the conversion project to handle these cases.

Class Map

Starting in FWD v3.3 the class map is no longer needed. The parser automatically calculates the class map in that version and later.

In order to resolve references to application classes (.cls source files) from within the same application, FWD uses a configuration file to map class names to relative filenames. This map is not used for lookup of built-in 4GL OO classes (e.g. Progress.Lang.Object), it is not used for lookup of classes in .NET assemblies and it is not used for lookup of built-in .NET classes (e.g. System.Object).

Before you can parse an application containing these 4GL OO class references you must create this class mapping configuration file. Since any 4GL OO .cls source file must by nature reference other objects, this is also needed to parse .cls source files.

The class mapping file must be placed in ./cfg/class_map.xml.


The format is as follows:

<?xml version="1.0"?>
   <entry key=”fully_qualified_package_and_class_name1” value=”relative_filename1” />
   <entry key=”fully_qualified_package_and_class_name2” value=”relative_filename2” />
   <entry key=”fully_qualified_package_and_class_name3” value=”relative_filename3” />

Each entry will have a key and a value that forms the mapping.

The key in the map is the fully qualified class name without the .cls and including the full package hierarchy with / characters as path separators.

On case-sensitive file systems, make sure that the filename matches the actual filename's case exactly.

The value is the relative filename for the .cls file, from the root of the project (including the .cls extension).


Assume you have the following 4GL OO files:

Filename Package (Qualifier from CLASS statement) Class Name
/src/my_app/some/package1/subA/MyObject1.cls package1/subA MyObject1
/src/my_app/some/package1/subB/MyObject2.cls package1/subB MyObject2
/src/my_app/other/MyObject3.cls   MyObject3
/src/my_app/what/package2/MyObject4.cls package2 MyObject4

Since the search algorithm is based on the PROPATH in the 4GL, the PROPATH must include the relative portions of the paths that are not part of the package names. In this example, the PROPATH will have to be configured including the following ./src/my_app/some/:./src/my_app/other:./src/my_app/what:

This will have to be configured in the p2j.cfg.xml in the propath global configuration option.

The resulting ./cfg/class_map.xml should look like this:

<?xml version="1.0"?>
   <entry key="package1/subA/MyObject1" value="./src/my_app/some/package1/subA/MyObject1.cls"/>
   <entry key="package1/subB/MyObject2" value="./src/my_app/some/package1/subB/MyObject2.cls"/>
   <entry key="MyObject3"               value="./src/my_app/other/MyObject3.cls"/>
   <entry key="package2/MyObject4"      value="./src/my_app/what/package2/MyObject4.cls"/>

On a Linux or UNIX system, the value must match the case of the filename exactly.

Tool for Generation of the class_map.xml

Starting in FWD v3.1, there is a tool called GenerateClassMap for creating the class_map.xml by inspective the file system. Run it from the project root directory, like this:

java -classpath p2j/build/lib/p2j.jar:.: com.goldencode.p2j.uast.GenerateClassMap ./abl/some/path/to/the/root/of/the/class/hierarchy/ ./abl/another/package/root/path/

The arguments are a kind of propath list, with each arg value being treated as a root package from which all contained .cls are
relative. You must pass at least 1 value. If all the OO classes are rooted at ./abl, then that would be a valid argument.

WARNING: if any classes are found, this will overwrite your cfg/class_map.xml file with no prompt! Back up any version you intend to save!

Skeleton Classes

The class map noted above makes it possible for the parser to properly parse an application's .cls files and any references to those “internal” classes.

But as noted above, there are 3 other kinds of classes that can be referenced in 4GL code:

  • built-in 4GL OO classes (e.g. Progress.Lang.Object)
  • classes in .NET assemblies (3 rd party code and/or your own .NET code)
  • built-in .NET classes (e.g. System.Object).

In each of these cases, the class names can be referenced and within those classes there are methods, properties, interfaces and other OO features that can be accessed directly.

When the code is in your own .cls file, the parser can recursively parse that .cls file to understand all the internal features of that class. That allows it to resolve all of those referenced. The parser uses the class map to find the .cls files and then it parses it as normal.

For the built-in 4GL classes and the two kinds of .NET classes, there are NO .cls source files that can be parsed to resolve all the feature references (e.g. methods or properties). In Progress, the OpenEdge compiler has access to the built-in classes AND on Windows it can also access any of the .NET assemblies or built-in classes that are installed on that system.

In FWD, we do not have access to the built-in 4GL OO classes. The tools may or may not be running on a Windows platform, but regardless of that fact, FWD does not directly inspect .NET assemblies or classes.

As a replacement, a set of skeleton classes and interfaces are created to provide an empty shell of an implementation in .cls files that can be recursively parsed just like the application's own .cls files. This “trick” makes it possible to easily extend the set of skeleton files whenever the application fails parsing because of a reference to an unknown built-in 4GL OO class or unknown .NET assembly or built-in class.

An example set of skeleton classes is available at Skeleton Classes Download. This may give developers a head start in creating skeletons. If you care to contribute your own skeletons back to the FWD project, please contact mailto:.


The root directory for the built-in 4GL OO and buillt-in .NET files is specified by the oo-skeleton-path configuration parameter in the p2j.cfg.xml. By default, this path is ./p2j/skeleton/.

These files must be placed in a hierarchy inside this skeleton directory. For example:


Built-in 4GL OO classes are in an oo4gl subdirectory and then have directories for the package structure. The built-in .NET classes are in a dotnet subdirectory and then have directories for the package structure. All packages and filenames match the case-sensitive version documented by Progress or Microsoft.

Any .NET assemblies that are not built-in to .NET are considered “application” code and the skeletons for those must be located in a ./assemblies/ directory off the project root ($P2J_HOME). This is an example:


Again, the package pathing is duplicated below the ./assemblies/ directory and each directory there must match the package components in name and case.


FWD provides some existing skeletons as they have been coded so far, but these are incomplete. Please see Downloads for the latest skeletons available.

As parsing failures are found, these will need to be expanded to include any classes or interfaces that are referenced but not already present. It is also possible that existing skeletons will need features added, as they may be incomplete.

The following is an example of ./p2j/skeleton/oo4gl/Progress/Lang/Object.cls:

class Progress.Lang.Object:

   define property next-sibling as Progress.Lang.Object

   define property prev-sibling as Progress.Lang.Object

   constructor public Object():
   end constructor.

   method public Progress.Lang.Object Clone():
   end method.

   method public logical Equals(input other as Progress.Lang.Object):
   end method.

   method public Progress.Lang.Class GetClass():
   end method.

   method public character ToString():
   end method.

end class.

Notice that this is an empty implementation, based on the documented API for the class. It is just enough to satisfy any parsing requirements without any real functionality being duplicated. When you create or modify any skeletons, make sure that the access control and data types match exactly with the API for that class.

The .NET classes are handled the same way, even though in .NET the actual interface would be quite different (since it would be done in C# or some other language that is not the 4GL). However, FWD still uses the 4GL .cls approach to “fake out” the parser into thinking these classes actually exist.

© 2004-2017 Golden Code Development Corporation. ALL RIGHTS RESERVED.