Skip to content

Explicit Conversions

avurro edited this page Mar 30, 2016 · 49 revisions

#under construction

this page is in update phase, excuse us for the inconvenience

Explicit conversion allows you to define your own control logic and / or processing data belonging to the source fields and assign them to the destination fields.
You can define conversions through the use of methods and you can do it in java, using the @JMapConversion annotation, in XML using the <conversion> node and in API through the Conversion class.
These give the same possibility and use the same parameters but in different format.

IMPORTANT!

  • The XML and API conversions has greater visibility of the equivalent annotated. For a specific relation is found first in the XML/API conversion method, if not found, will be done a search of the annotated conversion method if exist.
  • The conversion, once satisfied the relationship, replaces the basis mapping of the framework.
  • If there are more conversion methods satisfying the relationship, the first found will be considered.
  • The code must be written according the javassist specifications (if you are using the default bytecode generator).

The position of the conversion is independent from the configuration location
the conversions can be written in the Destination or Source, regardless the configuration location.
different combinations of configuration are permitted
You can also do a combination with all configuration format, for example: configuration with annotations and conversions in xml or vice versa.

The conversion between two fields is applied only when there is a relationship between them, follows an example of not applied conversion:

class Destination{                  class Source{
    @JMap
    private String id;                  private String id;
    @JMap("sourceField")
    private String destinationField;    private String sourceField;
    private String other;               private String other;

                                        @JMapConversion(from={"id"}, to={"other"})
                                        public String conversion(String str){
                                            return str;
                                        }
    // getters and setters... 	        // getters and setters...
}                                   }

Assuming to initialize the JMapper class in the following way:

JMapper jmapper = new JMapper<>(Destination.class, Source.class);

relations expressed:

  • Source.id -> Destination.id
  • Source.sourceField -> Destination.destinationField

The conversion instead is defined for the relation:

  • Source.id -> Destination.other

So in this case the conversion will never be applied, the same happens also for the configuration in XML and API format.

Configuration Parameters

After a short overview, we will see what are the configuration parameters common to all types of mapping.
We discuss them at a logical level, for use in detail see the relative section.
The following is a summary table:

Configuration parameter Description Default Value
name (required) name of the method Undefined
from (optional) names of the source fields ALL
to (optional) names of the destination fields ALL
type (optional) conversion type STATIC
avoidSet (optional) avoid set method usage false
The name is essential to distinguish the conversion methods between their.
The parameters from and to indicate which fields will be part of the conversion.
The from indicates the source, the to indicates the recipients, the default value for each is ALL which mean that the conversion will be applied to all related fields.
The Type allows you to define the conversion modes and has only two values: STATIC and DYNAMIC, it will be explained later.
avoidSet avoids the call to the set method for the destination instance if it's set to true.
These are the configuration parameters that can be used in all types of mapping, below we will see how to do for each type.

Annotation

This is an example of configuration in annotation style:

@JMapConversion (from = {"srcField1","srcField2"}, to={"destinationField"}, avoidSet=false, type=Type.STATIC)

The name is not required because is used the conversion method name.

XML

An example in xml format:

<conversion name="conversion" from="srcField1,srcField2" to="destinationField" avoidSet="false" type="STATIC">

API

configuration in API:

conversion("conversionName")
   .from("srcField1","srcField2")
   .to("destinationField")
   .avoidSet(false)
   .type(Type.STATIC)

How conversion works

The generated code is surrounded by a Try-Catch inserted with the aim of managing all the errors generated at runtime, such as NullPointerException.
The conversion methods can be of two types: STATIC and DYNAMIC, static when is used for a specific relationship, dynamic when the method's body change according the relationship that must be satisfied.
####Static conversion A conversion is called static when its body does not change with the defined relations.
Suppose we have two classes: Destination and Source, both with 3 fields of type String: d1, d2 and d3 for Destination and s1, s2, and s3 for Source and that you have configured s1 with d1, d2 with s2 and d3 with s3.
We want to define a conversion to be applied to all relationships, then the method will be defined (e.g. in annotation):

@JMapConversion(from={"s1","s2","s3"}, to={"d1","d2","d3"})
public String conversion(String source){
   return source + " converted";
}

Assuming that this method is defined in the Destination, the mapping generated at runtime will be as follows:

destination.setD1(destination.conversion(source.getS1()));
destination.setD2(destination.conversion(source.getS2()));
destination.setD3(destination.conversion(source.getS3()));

This conversion is the most common and also the most useful, allows to pool a manipulation that can affect more data.
In case there are any static methods with the same name, will be considered only the first one that meets the requirements.
##Dynamic conversion For dynamic conversion we means a method whose body is adapted to every relationship.
The conversion method defined is always one, but at runtime will be created a number of conversions equal to the number of relations.
The dynamic conversion allows to pool the basic logic varying only some values.
The placeholders that can be used in all configuration types are:

Placeholder Description
${source} source reference
${destination} destination reference
${source.type} source type
${destination.type} destination type
${source.name} source name
${destination.name} destination name
${destination.get} destination get method name
${destination.set} destination set method name
${source.get} source get method name
${source.set} source set method name

Example (in annotation):

@JMapConversion(from={"s1,s2,s3"}, to={"d1,d2,d3"}, type=Type.DYNAMIC)
public static String conversion(){
   return "return \"${destination.name} ${source.name}\";";
}

Method created at runtime:

public String fromS1ToD1(){
   return "d1 s1";
}

public String fromS2ToD2(){
   return "d2 s2";
}

public String fromS3ToD3(){
   return "d3 s3";
}

Generated mapping:

destination.setD1(fromS1toD1());
destination.setD2(fromS2toD2());
destination.setD3(fromS3toD3());

As you can see, the methods do not take input data because destination and source are referenced with the placeholder ${source} and ${destination}.

After this introduction we are going to see in detail how it works for every type of configuration and how to set one or more parameters for each of them.

Annotation

A single input parameter indicates the source field, two parameters indicate the destination field and source field, this case comes in handy when you want to act on a destination field already defined.
Example:

@JMapConversion(from={"sourceField"}, to={"destinationField"})
public String conversion(String destinationField, String sourceField){
   return destinationField +" "+sourceField+" converted";
}

XML

placeholder definition here

Example:

<jmapper>
  <class name="com.application.Source">
    <conversion name="conversion" from="sourceField" to="destinationField">
	return ${destination}+" "+${source}+" converted";
    </conversion>
  </class>
</jmapper>

API

Annotation's general instruction

The annotation that allows us to perform explicit conversion is the @JMapConversion.
JMapConversion has three parameters: from, to and type.

Field Description default value
String from names of the source fields ALL
String to names of the destination fields ALL
Type type conversion type STATIC
Boolean avoidSet (since 1.3.1) avoid set method usage false

The parameters from and to indicate which fields will be part of the conversion.
The from indicates the source, the to indicates the recipient, the default value for each is ALL.
ALL indicates that the conversion is to be applied to all related fields.
The Type is an enumeration of @JMapConversion that allows you to define the conversion modes and has only two values: STATIC and DYNAMIC, the default value of type is STATIC. avoidSet parameter set to true avoids the call to the set method.
Conversion modes are detailed in the following paragraphs.

XML's general instruction

In the xml definition the conversion is defined using the <conversion> node, whose attributes are: name, from, to and type.

Attribute Description default value
name method name undefined
from names of the source fields
(separated by commas:field,field2)
ALL
to names of the destination fields
(separated by commas:field,field2)
ALL
type conversion type STATIC
avoidSet (since 1.3.1) avoid set method usage false

The name attribute permits to define a name to the current conversion method, this attribute is mandatory.

IMPORTANT! make sure the name is unique for proper operation.

The parameters from and to indicate which fields will be part of the conversion, the from indicates the source, the to indicates the recipient, the default value for each is ALL, ALL indicates that the conversion is to be applied to all related fields.
the Type is an attribute that allows you to define the conversion modes, and has only two values: STATIC and DYNAMIC, the default value of type is STATIC.
Conversion modes are detailed in the following paragraphs. avoidSet attribute set to true avoids the call to the set method.

In annotation

Consists in the definition of the conversion method using the following conventions:

  • the input field to the method must be the same type of the source fields
  • the output field to the method must be the same type of destination fields

Example:

@JMapConversion(from={"anInteger"}, to={"aString"}, type=Type.STATIC)
public String conversion(Integer anInteger){
   return anInteger.toString();
}

Assuming that the method is defined in the Destination, JMapper generate the following mapping:

destination.setAString(destination.conversion(source.getAnInteger()));

If you want act even on the present value of the destination field, just add an input parameter, keeping in mind that the first field on the left is the destination and the second is the source:

@JMapConversion(from={"anInteger"}, to={"aString"}, type=Type.STATIC)
public String conversion(String aString, Integer anInteger){
   if(aString == null) aString = "initialized";
   return aString + anInteger;
}

The generated mapping will be as follows:

destination.setAString(destination.conversion(destination.getAString(),source.getAnInteger()));

In XML

Consists in the definition of the conversion node, whose value is the body of the method to create.
To refer to the source field and destination field you must use the following placeholders:

  • ${source} to refer to the source field
  • ${destination} to refer to the destination field

Example:

<conversion name="conversion" from="anInteger" to="aString" type="STATIC">
   return ${source}.toString();
</conversion>

Method created at runtime:

public String conversion(Integer source){
   return source.toString();
}

Mapping generated:

destination.setAString(conversion(source.getAnInteger()));<br>

If you want to act even on the destination, just use the placeholder: ${destination}.

<conversion name="conversion" from="sourceField" to="destinationField">
   return ${destination}+" "+${source}+" converted";
</conversion>

Method created at runtime:

public String conversion(String destination, Integer source){
   return destination +" "+ source +" converted";
}

Mapping generated:

destination.setAString(conversion(destination.getAString(),source.getAnInteger()));

In annotation

Consists in the definition of the conversion method using the following conventions:

  • the method must be: public static
  • should not receive input parameters
  • must return a string containing the dynamic mapping

Example:

@JMapConversion(from={"anInteger"}, to={"aString"}, type=Type.DYNAMIC)
Public static String conversion(){
 return "${destination.type} result = ${source} == 2?\"${source.name}\":\"${destination.name}\";"
     +  "return result + ${destination};";
}

Method created at runtime:

public String FROManIntegerTOaString(String destination,Integer source){
   String result = source == 2? "anInteger":"aString";
   return result + destination;
}

JMapper generate the following mapping:

dest.setAString(fromAnIntegertoAString(dest.getAString(),source.getAnInteger()));

In XML

The placeholders can also be used in xml, the above example is repeated in xml format:

<conversion name="conversion" from="anInteger" to="aString" type="DYNAMIC">
   ${destination.type} result = ${source} == 2?"${source.name}":"${destination.name}";
   return result + ${destination};
</conversion>

Method created at runtime:

public String FROManIntegerTOaString(String destination,Integer source){
   String result = source == 2? "anInteger":"aString";
   return result + destination;
}

Mapping generated:

dest.setAString(fromAnIntegertoAString(dest.getAString(),source.getAnInteger()));
Clone this wiki locally