Transformation Definition Reference Transformations in ELIDE are defined by Java classes of a particular form. This document provides a reference transform with information on the use of and motivation behind features available to the transform developer. The transform shown defines the semantics of a hashable<> modifier, which adds an equals() and a haschCode() method to the target class, customized to the given class. This modifier optionally accepts field names as arguments, which specify the fields of the class to be considered in the equals() and hashCode() methods. All such fields must have a hashCode method (ie. must at least be derived from Object). If no arguments are given, all fields of the target class are considered by equals() and hashCode(). --------------------------------- Anatomy of a Transform: --------------------------------- // (1) ELIDE-specific imports import ca.ubc.cs.elide.nodes.*; import ca.ubc.cs.elide.*; import java.util.*; public class Hashable extends Transform // (2) Inherit from ca.ubc.cs.elide.Transform { /**** First Transformation Method ****/ satisfies // (3) Satisfies info for dependency resolution requires // (4) Requires info for dependency resolution uses // (5) Tell the system that an equals<> transform is added here public void addEquals(ClassNode target, List args) // (6) parameters { // (7) Execute this block once only for the given args: once { // (8) Add another transform to the target node target.addTransform(new TransformNode("equals", args)); } } /**** Second Transformation Method ****/ satisfies requires public void addHashCode(ClassNode target, List args) { once { // (9) extend the target class with a new method target.extend( %{ public int hashCode() { // (10) #{}# escapes from the string literal return #{hashExpression(target, args)}#; } }% } } /**** (11) Helper Method ****/ private String hashExpression(ClassNode target, List args) { StringBuffer expr = ""; FieldNode[] fields; if( args.isEmpty() ) fields = target.getFields(); else fields = args.toArray(); for(int i = 0; i < fields.length; i++) { expr.append(field[i].getName()+".hashCode()); if( i != fields.length - 1 ) expr.append(" + "); } return expr.toString(); } } ---------------------------------- (1) These imports are required for any Transform definition. Class Transform is found in ca.ubc.cs.elide and all of the nodes are found in ca.ubc.cs.elide.nodes. See the javadoc for more information. (2) All transforms must inherit directly or indirectly from ca.ubc.cs.elide.Transform. (3) Each transformation method (i.e. addMutator() and addEquals() in the example must register as part of the dependency system (LINK THIS). This is done by adding one or more satisfies<> modifiers to a given transformation method. Any methods of a transformation without satisfies<> will not be called directly by the system (but may be called by registered transformation methods). The required argument to satisfies<> is treated as a string literal and is used to describe what the transformation method does. For example, since both transformation methods above add methods to a class, they both have satisfies attached. Note that these strings are not predefined by ELIDE, but are chosen by the user. (4) requires<> is the complement to satisfies<>. Zero or more requires<> modifiers can be added to ensure that all transformation methods that satisfy a given string literal argument will be executed before the current method. In our example, both transformation methods require 'fieldDefinition' transformations to be performed first, since they use field information of a class. ELIDE will raise an error if a circular dependency is encountered. (5) ELIDE allows transformation methods to add other transformations to nodes, allowing for greater reuse of transform definition code. To ensure correct operation of the system, a uses<> modifier should be added to such transformation methods. This notifies the system that the added transformation is being used even if the base code contains no instances of it. In our example, hashable<> uses the already-defined equals<> modifier and thus has uses attached. (6) The parameters passed to transformation methods provide the link between the use of modifiers in base code and the semantics of the transformation. The first parameter, which must be of a type derived from ChildBearingNode, corresponds to the source construct to which the modifier is attached. This allows modifiers to be "overloaded" so that they can provide different semantics depending on whether they are attached to, for example, a field or a method. The remaining parameter(s) reference the arguments passed to the modifier. If the second parameter is a List, any number of parameters may be passed to the modifier and these will be collected into this List. If there are N String parameters following the target parameter, the modifier may be passed N arguments, and these will be received by the transformation method in the order given. (7) The once<> modifier is used to specify that a given block of transform code should only be executed once for each set of arguments passed in. This is useful when, for example, a modifier attached to possibly many fields of a class has an effect on that class that should only occur once, such as adding an implemented interface statement. In our example the use of once<> is unnecessary, since it is assumed that the hashable<> modifier will only ever be added once to a given class. (8) Adding the equals<> tranform to the target class node from within hashable<>. See for more details. (9) The typical way to add methods or fields to a class is with ClassNode.extend(). The %{}% denotes a multiline string literal. See for more details. (10) The string literal can be used as a template by adding escapes denoted by #{}#. This allows developers to use the results of helper methods or the values of variables directly in the template. In the example, the String value returned by the helper hashExpression() is used to build the hashCode() body. Helpers used in this fashion must obviously return appropriate values: Strings that make sense in the given context. (11) Any number of helper methods may occur in transform definitions. They are distinguished from transformation methods in that they do not have dependency modifiers (satisfies<>, requires<>) attached.