SDA SE Wiki

Software Engineering for Smart Data Analytics & Smart Data Analytics for Software Engineering

User Tools

Site Tools


Conditional Tranformations (CTs)

The technology which is used to transform the factbase (and therefore the source code) is called “Conditional Transformations” (or short: CTs). In this section we will describe how to define basic and parameterized CTs, how to run them manually and how to examine the changes they did on the factbase. To save time while writing conditions and transformations you can use the Source Templates feature.

Basic CTs

Conditional Transformations are named specifications of program transformations. They consist of a name, a precondition and a transformation. If the precondition holds the transformation is executed.

A CT is represented in JTransformer by the Prolog fact ct/3. Its first argument is the head of the CT, the second one is its condition and the third one is the transformation description.

Below is a simple 'addField' CT. It creates the field 'newField' in the class 'MyClass' and sets the field's visibility to public. If you don't use the JT_Tutorial project just replace 'MyClass' by the name of any Class in your project.

ct( addField,                                           % HEAD
    (                                                   % CONDITION
      classT(Class, _, 'MyClass', _, _),                % Get the ID belonging to class 'MyClass'
      not(externalT(Class)),                            % ... which is no external class
      not( fieldT(Field, Class, _, 'newField', _) ),    % Make sure, that no field with the
                                                        %   name 'newField' exists
      basicTypeT(IntType, int),                         % Get the ID belongig to type int
                                                        
      new_id(Field),                                    % Field is a yet unused ID
      new_id(Modifier)                                  % ... and so is Modifier
    ),
    (                                                         % TRANSFORMATION:
      add(fieldT(Field, Class, IntType, 'newField', null)),   % Add the field
      add(modifierT(Modifier, Field, 'public')),              % Make it public
      add_to_class(Class, Field)                              % Let the class know, that it has a new member
    )
).

The head can be any Prolog atom. It can be followed by an optional parameter list and can be used to invoke the CT and pass parameters to the invokation.

The precondition describes structural requirements on the program to be transformed. It consists of Program Element Facts (PEF) and invocations of Prolog predicates connected by the usual boolean operators written in Prolog syntax:

  • 'a AND b' is written 'a, b'
  • 'a OR b' is written 'a; b'
  • NOT(a) is written 'not(a)'

Every non-negated PEF in the precondition is a check whether a corresponding program element exists in the internal representation of the program that we want to transform. In addition, the predicate 'new_id(Id)' is used to generate unique identifiers for new PEFs to be created in the transformation part. It binds the free variable Id to a new, unique identifier. In the example above, this is used to generate the identifier for the PEF representing 'newField'.

The transformation part can contain a conjunction of the following operations that transform the fact representation as their names suggest:

  • add(PEF)
  • delete(PEF)
  • replace(oldPEF,newPEF) % replaces oldPEF with newPEF
  • replace(newPEF) % updates element with the ID of newPEF

There are also some useful commands, which you can use in a transformation, under Java Transformations.

Parametrized CTs

You can parametrize a CT head by appending comma-separated variables enclosed by parenthesis. The parameters can be used to pass values to a CT application and to link multiple CTs that are invoked together. If you just want to replace a specific call (and not all calls) you can use the parameterized version and bind the paramter “CallId” to the ID of the specific call.

ct( addField(FName,CName),                              % HEAD with parameters
    (                                                   % CONDITION
      classT(Class, _, CName, _, _),                    % Get the ID belonging to class 'MyClass'
      not(externalT(Class)),                            % ... which is no external class
      not( fieldT(Field, Class, _, FName, _) ),         % Make sure, that no field with the
                                                        %   name 'newField' exists
      basicTypeT(IntType, int),                         % Get the ID belongig to type int
                                                        
      new_id(Field),                                    % Field is a yet unused ID
      new_id(Modifier)                                  % ... and so is Modifier
    ),
    (                                                         % TRANSFORMATION:
      add(fieldT(Field, Class, IntType, FName, null)),        % Add the field
      add(modifierT(Modifier, Field, 'public')),              % Make it public
      add_to_class(Class, Field)                              % Let the class know, that it has a new member
    )
).


ct( makeFieldPrivate(FName, CName),                                   % HEAD with parameters
    (                                                                 % CONDITION
       classT(Class, _, CName, _, _),                                 % Get the class ID...
       fieldT(Field, Class, _, FName, _),                             % ... and the field ID
       modifierT(Modifier, Field, Visibility),                        % Get the modifier...
       ( Visibility = 'public'                                        % ...and make sure it is public ...
       ; Visibility = 'protected'                                     % ... protected ...
       ; Visibility = 'package')                                      % ... or package visibility
    ),
   (
    replace( modifierT(Modifier, Field, Visibility ),                 % replace the selected modifier with private
             modifierT(Modifier, Field, 'private') )
   )
 ).

Run CTs

To run a CT make sure it had been consulted and use the 'apply_ct(CT_Name)' predicate in the Prolog Console View. For instance, you can apply the 'addField' CT by executing the query:

?- apply_ct(addField).

To run several CTs in sequence, consult them and use the predicate 'apply_ctlist([ctHead1,…])'. The example below invokes the two parametrized CTs defined above, passing the desired arguments:

?- apply_ctlist( [ addField('testField', 'MyClass'), 
                   makeFieldPrivate('testField','MyClass') ]).

In either case, the invoked CTs are applied to all matching facts. So if you type: “apply_ct(addField('field123', Class)).”, the field 'field123' will be added to all classes.

Running a CT doesn't overwrite your existing Java source code, it only modifies your internal factbase. See view transformation results.

Note that substitutions computed for free variables in a CT invocation are not displayed in the Prolog console. In most cases there would simply be too many of them. Since all results are computed at once, backtracking on a CT application (by typing ';') will produce no new results.

View Transformation Results

Running CTs does NOT overwrite the existing source code. Only the internal factbase is modified. You can inspect it before deciding whether to save its new state as source code.

To inspect the results of the transformation you can use the Factbase Inspector (FBI). Open the FBI for a class that you expect to have been modified by the transformation. The new FBI's contents should look like this:

The FBI shows all different types of nodes (added, modified, deleted):

By double-clicking any node in the FBI you can either

  • see its original source code in the editor or
  • see its reverse-engineered source code (if it was added by a ct, or generally has no source code) or
  • open a “compare view” showing the original and the transformed source code (if the node was modified by a CT).

The double-click is actually just a context-sensitive shortcut for different actions of the FBI context menu. The context menu offers additional actions to further inspect the results of your transformation. Just give it a try.

Save Transformation Results

CTs only modify the internal factbase. Your source code is left unmodified. To write the changes back to java code you can use JT's “Reverse Engineering” feature.

Undoing Transformations

If inspection of the factbase reveals some error in the perfomed CT its execution can be undone. There is currently no GUI for this. You have into type the following to your Prolog console:

rollback.

See also: Transaction API.

Logging

By default, a CT will only have true or false as output. If you want to know more, you can enable logging for the CTs and see which facts were added or deleted. Call the predicate enable_ct_logging/0. To disable the logging you can use disable_ct_logging/0.

research/jtransformer/tutorial/cts.txt · Last modified: 2018/05/09 01:59 (external edit)

SEWiki, © 2019