Penrose uses BeanShell
expressions to perform mappings/transformations between attributes and source fields.
Mapping
To map a source field to an attribute, you can consider the source as a Java object and the fields as the object properties.
To map an attribute back to a source field, you can simply refer the attribute as a Java object.
<entry ...> <at name="mail"> <expression>users.email</expression> </at> <source name="users"> <field name="email"> <expression>mail</expression> </field> </source> </entry>
In the above example, the users' email field is mapped to the mail attribute, and the mail attribute is mapped back to the users' email field.
Transformation
Within the <expression>...</expression> tags you can also perform any data transformation. The transformation can be a one-line expression:
<at name="cn"> <expression>users.firstName+" "+users.lastName</expression> </at>
or it can be a script:
<field name="firstName"> <expression> // if cn is undefined, return null if (cn == void || cn == null) return null; // find the space between first name and last name in cn int i = cn.indexOf(" "); // if there's no space (i less than 0), return null if (i < 0) return null; // return the first part of cn return cn.indexOf(0, i); </expression> </at>
The requirement is that a script has to return a value, which can be either null, an object, or a collection.
Handling Multiple Values
When mapping from source field to attribute, if the field only has a single value, the attribute will get the field's value as a Java object. If the field has multiple values, the attribute will get the values as a Java collection. Since BeanShell is an untyped language, you don't have to type-cast the object to the collection type. The same thing happens when mapping the attribute back to source field.
From an expression you can return a single value or multiple values. To return a single value you simply return an object. To return multiple values you return a collection.
<at name="mail">
<expression>users.email</expression>
</at>
In this above example, the users.email expression will handle both single and multiple field values, and may return single or multiple attribute values, depending on the actual data.
<at name="description"> <expression> if (users == void || users == null) return "User has no email."; if (users.email instanceof Collection) return "User has "+users.email.size()+" emails"; return "User has 1 email."; </expression> </at>
In the above example, the script returns a message stating how many email addresses the user has.
Looping
Sometimes you need to iterate through a collection. You can do it manually:
<at name="uniqueMember"> <expression> if (users == void || users == null) return null; Collection list = new ArrayList(); for (Iterator i=users.email.iterator(); i.hasNext(); ) { String email = (String)i.next(); list.add("mail="+email+",ou=Customers,dc=Example,dc=com"); } return list; </expression> </at>
or, you can use the foreach tag attribute:
<at name="uniqueMember"> <expression foreach="users.email" var="email"> "mail="+email+",ou=Customers,dc=Example,dc=com" </expression> </at>
The foreach tag will work both on single-valued or multi-valued fields, and will generate single-valued or multi-valued attributes accordingly.