Oracle® Application Server ProcessConnect User's Guide 10g (9.0.4) Part Number B12121-01 |
|
This chapter describes how to design advanced transformations. Common transformation errors and transformation limitations are also described.
This chapter contains these topics:
Transformation supports implicit conversions between datatypes. Implicit datatype conversions are carried out between datatypes without any explicit datatype conversion function being called. This enables you to use variables and parameters of one type where another type is expected.
The following implicit datatype conversions are supported by transformation:
Any Oracle Application Server ProcessConnect core datatype can be converted to a string datatype implicitly. For example, a source item of an integer datatype can be converted to a string datatype without explicitly invoking a datatype transformation map for conversion.
A source and target item of a scalar datatype can be bound to the source and target of the invoked transformation map, which can be any of its base types (upcasting). Scalar datatypes can be inherited only by restriction. Therefore, the subtype is always a specialized form of the base type. For example, consider the type hierarchy of a token derived from a string. A token can be assigned to a string because it is a specialized form of a string.
Similarly, a source item and target item of a scalar type can be bound to the source and target of the invoked transformation map, which can be one of its subtypes (downcasting) (for example, assigning a string to a token). In future releases, this feature may not be supported.
In addition to both of these implicit datatype conversions, there are a number of numeric implicit conversions possible:
These conversions are allowed since there is no loss of data. Implicit conversion is applicable to both source and target parameters.
"Example 1: Implicit Conversion for Source and Target Parameters" and "Example 2: Implicit Conversion of Source and Target Parameters in the Same Datatype Transformation Map" illustrate the implicit conversion scenarios in transformation maps. These examples help you identify the correct datatype transformation map to invoke in places where implicit conversions are allowed. An entire list of implicit and explicit conversions allowed by transformation can be viewed in Table 14-1.
In Figure 14-1, the total of Invoice
, which is of type double
, gets copied to the total of PO
, which is of type string
.
To perform the transformation operation in Figure 14-1, you must select the appropriate copy
datatype transformation map to be invoked. In this case, there are two valid datatype transformation maps that can be invoked:
Selecting the first copy
transformation map results in implicit conversion of a source parameter. In this case, the total
member of Invoice
of type double
is bound to the source parameter from
of the copy
datatype transformation map of type string
. This is a valid binding since a double datatype can be implicitly converted to a string datatype. The target binding does not need conversion since their datatypes are the same.
Selecting the second copy
transformation map results in implicit conversion of a target parameter. In this case, the total
member of PO
of type string
is bound to a target parameter of a datatype transformation map of type double
. This is a valid binding since a double
datatype can be implicitly converted to a string
datatype. The source parameter does not need conversion since their datatypes are the same.
Figure 14-1 illustrates two important points:
{ ... copy (from= Invoice::/total) -> (to= PO::/total); ... }
In Figure 14-2, the price
of Line-Item
, which is of type float
, is multiplied with quantity
, which is of type integer
, to obtain the total. The total obtained is copied to the item-total
of Item
which is of type string
.
To perform this transformation, the following datatype transformation map is invoked:
multiply(first=float, second=float -> result=float) ;
In this case, price
is bound to the first source parameter for the datatype transformation map and quantity
to the second source parameter. For the target parameter result, the item-total
is bound. The quantity
of Line-Item
, which is of type integer
, gets implicitly converted to datatype float
of the second parameter. The result, which is of type float
, is implicitly converted to a string
type.
{ ... multiply (first = Line-Item::/price, second= Line-Item::/ Quantity) -> (to= Item::/Item-Total); ... }
In Figure 14-2, the code
of Line-Item
, which is of type string
, is copied to the code
of Item
, which is of type integer
. To perform this transformation invocation, the following copy
datatype transformation map is invalid since, there is no implicit conversion from the source item, code
of Line-Item
, which is of type string
to the source of the copy
datatype transformation map, which is of type integer
.
copy(from=integer -> to=integer);
{ ... copy (from= Line-Item::/Code) -> (to= Item::/Code); ... }
To invoke the copy
datatype transformation map, you must first convert the code
of Line-Item
to a value of datatype integer
. Invoking the datatype transformation map stringToInteger
with the code
bound to the source and a map variable (for example, integerValue
of type integer
bound to the target) results in the required conversion. This map variable, integerValue
, can then be substituted in place of the source item code
in this datatype transformation map.
{ ... stringToInteger(sourceString= Line-Item::/Code) -> (resultInteger= integerValue); copy (from = integerValue) -> (to= Item::/Code); ... }
Table 14-1 shows the datatype conversion matrix of allowable implicit and explicit conventions. The following naming conversions are used.
Table 14-1 Datatype Conversion Matrix
This section describes common user errors in designing transformations.
This section contains these topics:
You must check for the existence of an optional datatype member of any datatype in the instance. If the member is not present in the source instance, and there is a rule for copying the source item to the target item, an empty element is created in the target instance.
Under datatype Address
, street1
is an optional member.
The incorrect design is to copy the source without checking for the existence of the datatype member in the source instance. The following transformation rule:
copy( from=street1 -> to= street1)
creates an empty street1
element in the target instance. For example, the source instance:
<Address> </Address>
results in the following target instance:
<Address><street1/></Address>
Use the Exists
function to check for existence of the optional datatype member in the instance. This prevents having an empty element in the target instance:
if ( Exists ( street1 ) ) { copy( from =street1 -> to= street1); }
Check for the existence of any datatype member of a datatype whose model group is choice in the instance as an optional member. If the member is not present in the source instance, an empty element is created in the target instance.
The street1
and street2
datatypes under datatype Address
are choice members.
The incorrect design is to copy without checking for the existence of the members in the source instance. The following transformation rule:
copy( from=street1 -> to= street1); copy( from=street2 -> to= street2);
creates an empty street1
or street2
element in the target instance. For example, the source instance:
<Address><street1>Oracle Parkway</street1></Address>
or
<Address><street2>Oracle Parkway</street2></Address>
results in the following target instance:
<Address><street1>Oracle Parkway</street1> <street2/></Address>
or
<Address><street1/> <street2>Oracle Parkway</street2></Address>
Use the Exists
function to check for the existence of an optional datatype member of a datatype in the instance. This avoids the creation of an empty element in the target instance.
if ( Exists ( street1 ) ) {copy( from=street1 -> to= street1);} if ( Exists( street2 ) ) {copy( from=street2 -> to= street2);}
See Also:
"Creating a Complex Datatype" for conceptual details about the choice model group |
If you have a datatype member of multiple occurrences, you must access it with an iterator. Otherwise, you receive a runtime error if there are multiple elements in the instance. This error typically occurs when you either do not know that the member being accessed has multiple occurrences, or you wrongly assume that you are accessing the first (or last) instance of the datatype member by default.
Target Address
has multiple streets mapped from multiple source streets in source Addr
.
You are unaware that Addr
has source streets (st
) of multiple occurrences. For example:
CopyAddrToAddress (Addr fromAddr -> Address toAddress) {copy(from=fromAddr::./st -> to=toAddress::./street);}
You receive a runtime error if st
appears multiple times in the Addr
instance.
The correct design is to use an iterator:
CopyAddrToAddress (Addr fromAddr -> Address toAddress) {for each(streets=fromAddr::./st) {copy(from=iterator streets -> to=toAddress::./street)} }
Transformation mandates the invocation of a separate user-defined datatype map from within an iterator to map a complex target member of multiple occurrences from a complex source member of multiple occurrences. A common error is to create direct mappings to complex target members within an iterator without invoking a separate datatype transformation map. In this case, the execution results in creation of a target structure that is unintended and does not match the corresponding type definition. This causes failure in native event validation or in later transformations.
The target purchase order with multiple complex members of type item
is mapped from source Invoice
with multiple complex members of type line-item
. Each target item's content (identifier
, quantity
, and cost
) is mapped from the corresponding source line-item content (id
, qty
, and cost
).
Figure 14-3 shows this scenario.
The incorrect design is to create direct mappings to complex target members within an iterator without invoking a separate datatype transformation map, as depicted in the following mapping logic:
for each (lineItems = inv::./line-item) {copy (from=iterator line-items::./id -> to=po::./item/identifier); copy (from=iterator line-items::./qty -> to=po::./item/quantity);}
This creates a single item with as many identifier
and quantity
values as the number of source line-items. For example, this source instance:
<invoice><line-item><id>001</id> <qty>1</qty></line-item> <line-item><id>002</id> <qty>2</qty></line-item></invoice>
results in the following target instance:
<po> <body> <item> <identifier>001</identifier> <identifier>002</identifier> <quantity>1</quantity> <quantity>2</quantity> </item> </body> </po>
An invocation of a separate datatype transformation map is required within the iterator to fully populate the target item before adding to the target PO
. This is so that it does not require updating with child content (for example, quantity
and cost
) later.
The datatype transformation map invoked within the iterator maps the members of the target item from the members of the source line-item. For example:
copyInvoiceToPO (Invoice inv -> PO po) { .... for each (lineItems = inv::./line-item) { copyLineItemToItem (lineItem=iterator line-items -> item=po::./item); } .... } copyLineItemToItem (lineItem srcLineItem -> item targetItem) { copy (from=srcLineItem::./id -> to= targetItem::./identifier); copy (from=srcLineItem::./qty -> to= targetItem::./quantity); copy (from=srcLineItem::./cost -> to= targetItem::./cost); }
Transformation does not support incremental populating of a complex datatype using submaps. If you invoke a submap that passes in a complex datatype as a target parameter, a new instance of that complex datatype is created by that submap invocation. If the same complex datatype parameter is passed to several submaps, a new instance is created every time. If using submaps, therefore, populate the complex datatype only once. It is permissible to populate the members of the complex datatype one-by-one by calling submaps. The error typically occurs when you bunch several members together and create submaps to populate those sets of members.
You want to transform a detailed address to a summarized address. A detailed address has the following format:
<address><street1>106 Main Street</street1> <street2>Apt #200</street2> <city>Anytown</city> <state>CA</state> <zip>10011</zip></address>
A summarized address has the following format:
<address><street>106 Main Street, Apt #200<street> <location>Anytown, CA 10011</location></address>
The incorrect design is to pass the same target address to two submaps expecting that they populate different parts of the address.
mapAddress (DetailedAddress sourceAddr -> SummaryAddress targetAddr) {mapStreetAddress(addr1=sourceAddr -> addr2=targetAddr); mapLocation(addr1=sourceAddr -> addr2=targetAddr);} mapStreetAddress(DetailedAddress addr1 -> SummaryAddress addr2) {//concatenates street1 and street2 and populates the // street in addr2} mapLocation(DetailedAddress addr1 -> SummaryAddress addr2) {// concatenates city, state and zip and populates the // location in addr2}
The correct design is to pass only the members of the target address to the submaps.
mapAddress (DetailedAddress sourceAddr -> SummaryAddress targetAddr) {formStreetAddress(addr1=sourceAddr -> street=targetAddr::./street); formLocation(addr1=sourceAddr -> location=targetAddr::/location);} formStreetAddress(DetailedAddress addr1 -> string street) {// concatenates street1 & street2 and returns result} formLocation(DetailedAddress addr1 -> string location) {// concatenates city, state & zip and returns result}
All mandatory datatype members of a target datatype must be populated. Otherwise, transformation generates an invalid target instance.
street1
is a mandatory member under datatype Address
.
The incorrect design is to have no rule defined or no rule executed at runtime for the mandatory datatype member of the target datatype model.
There is currently no alternative other than ensuring that all target mandatory datatype members get populated with the map. The user interface tool shows the members mapped by a transformation map that helps determine if any mandatory target member remains unmapped.
This section describes transformation limitations.
This chapter contains these topics:
The Oracle Application Server ProcessConnect user interface tool currently only supports global scope variables within transformation statements. You cannot define a variable within a transformation block in a map (for example, within a transformation iterator or a transformation condition). A map variable is accessible throughout the map and can be used from within any block in the map.
Assume that you must compute the total cost of all the line-items within an invoice (if there are any line-items in the source). You go over each of these line-items and add the price of each into a temporary variable that is initialized to zero. Finally, you copy the total price into the totalCost
field within the purchase order (PO
).
copyInvoicePOMap [DatatypeMap](AppEventDatatype Invoice invoice -> BusinessEventDatatype PO po) { if(Exists(invoice::./lineitem) { int totalPrice;copy(from=0->to=variable totalPrice);for each (lineItems = invoice::./line-item) {copy(from=line-item::./price -> to=item::./price); copy(from=line-item::./quantity -> to=item::./quantity); add(first=line-item::./price second=variable totalPrice -> result=totalPrice);} copy(from=variable totalPrice->to=po::./totalCost); } }
Declare totalPrice
outside all transformation blocks within the map; in this case, outside the Exists
condition in which it was declared.
copyInvoicePOMap [DatatypeMap](AppEventDatatype Invoice invoice -> BusinessEventDatatype PO po) { int totalPrice; if(Exists(invoice::./lineitem) { copy(from=0->to=variable totalPrice); for each (lineItems = invoice::./line-item) {copy(from=line-item::./price->to=item::./price); copy(from=line-item::./quantity->to=item::./quantity); add(first=line-item::./price second=variable totalPrice->result=totalPrice);} copy(from=variable totalPrice->to=po::./totalCost); } }
You cannot currently change the position of already-defined transformation statements in the Oracle Application Server ProcessConnect user interface tool. You can only define new rules to add to the end of the list of statements, or inserted within the list of statements.
Assume you have the following set of statements:
for each (lineItems = invoice::./line-item) { if ((line-item::./quantity >= 5) OR (line-item::./line-total > 1000)) {copy(from=line-item::./quantity -> to=item::./quantity);concat(firstString=variable tmpId,secondString=line-item::./description -> resultString=item::./uid); }
You then decide to copy the field quantity
to the target even though the quantity is less than five. This means you must move the related copy
operation outside the IF condition. This is not currently possible in the Oracle Application Server ProcessConnect user interface tool.
Delete the copy
statement from the set of transformation statements. Then, insert a new statement for performing the copy above the IF condition.
After you have populated a member of the target, you cannot overwrite it. To populate any member, you have to invoke some type of rule; since every rule invocation results in a new instance being created, you are creating multiple instances of the member if you invoke the rule multiple times. After you have initialized the member with a given instance, however, you cannot overwrite that instance.
While mapping an invoice to a purchase order, you want to determine whether to apply a discount to the purchase order. You have multiple condition statements that, if true, result in a discount being applied:
if (invoice::./total > X) {subtractInteger(first=invoice::./total, second=1000, result=po::./total);} if (invoice::./quantity > Y) {subtractInteger(first=invoice::./total, second=500, result=po::./total);} ...
If it is possible that more than one condition can evaluate to true, you receive a result that was not intended. Multiple total elements get created in the purchase order, each with a particular discount applied.
The only workaround is to ensure that a particular rule that populates a member gets executed only once. In this example, structure your logic so that the conditions are mutually exclusive (that is, only one of them evaluates to true at runtime).
This section describes NOT operator workarounds.
You cannot currently use the logical NOT operator while defining condition expressions. The only logical operators supported by transformation are =, !=, <, <=, >, >=, and Exists. To combine simple condition expressions to create more complex ones, the AND and OR operators can be used.
You want rule1
to be invoked if the following condition is true:
NOT((a > 5) OR (b <= 2))
Otherwise, you want rule2
to be invoked. You ideally want to design it as follows:
if (NOT((a > 5) OR (b <= 2))) {rule1;} else {rule2;}
However, the NOT operator is currently unsupported, and you must design this scenario differently.
You can substitute the rules in the IF and ELSE blocks and remove the NOT operator. This results in:
if ((a > 5) OR (b <= 2)) {rule2;} else {rule1;}
Although this strategy works, it is not useful for the majority of cases where you do not have an ELSE block with which to start. For example, if the original condition is as follows:
if (NOT((a > 5) OR (b <= 2))) {rule1;}
you cannot convert this as follows:
if ((a > 5) OR (b <= 2)) { } else {rule1;}
This is because transformation does not permit empty blocks such as the IF block shown in this example.
The workaround is typically to use logical arithmetic to negate the condition. You can use the following rule to negate the condition:
NOT(a OR b) = NOT a AND NOT b
Therefore:
NOT((a > 5) OR (b <= 2)) -> NOT (a>5) AND NOT(b<=2) -> (a<=5) AND (b>2)
You can write the condition as follows:
if ((a<=5) AND (b>2)) {rule1;} else {rule2;}
The one case where this logical negation of the condition fails is in the case of the operator Exists
. If you want to model the following:
if (NOT(Exists(a))) {rule1;}
Use the following workaround:
boolean exists_var; if (Exists(a)) {exists_var=true;} else {exists_var=false;} if (exists_var = false) {rule1;}
This section describes how to design advanced transformation iterator scenarios.
This section contains these topics:
This section describes structure-preserving mappings. These are mappings in which the datatype structure of the target is exactly the same as that of the source.
A multiple occurrence scalar target member is mapped from a multiple occurrence scalar source member.
Target Address
has multiple streets mapped from multiple source streets in source Addr
, as shown in Figure 14-4.
CopyAddrToAddress (Addr fromAddr -> Address toAddress) { for each(streets=fromAddr::st) { copy(from=iterator streets -> to=toAddress::street) } }
A multiple occurrence complex target member is mapped from a multiple occurrence complex source member.
Target PO
with multiple items is mapped from source Invoice
with multiple line-items. Each target item's contents (identifier
, quantity
, and cost
) are mapped from the corresponding source line-item content (id
, qty
, and cost
), as shown in Figure 14-5.
Invocation of a separate datatype transformation map is required within the iterator to fully populate the target item before adding to the target PO
.
The datatype transformation map invoked within the iterator maps the members of the target item
from the members of the source line-item
.
copyInvoiceToPO (Invoice inv -> PO po) { .... for each (lineItems = inv::./line-item) { copyLineItemToItem (srcLineItem=iterator line-items -> targetItem=po::./item); } .... } copyLineItemToItem (lineItem srcLineItem -> item targetItem) { copy (from=srcLineItem::id -> to= targetItem/identifier); copy (from=srcLineItem::qty -> to= targetItem/quantity); copy (from=srcLineItem::cost -> to= targetItem/ cost); }
A source member with multiple occurrences is mapped to two different target members with multiple occurrences based on a condition. For example, a source invoice has multiple line-items that each have a member called availability
. This member's value indicates whether the line-item is in stock or not. The target GroupedPO
has a structure where all the in-stock line-items are grouped under inStock
and the out of stock items are grouped under outOfStock
, as shown in Figure 14-6.
A condition must be defined within the iteration of the source line-item to complete the mapping to the appropriate target member.
copyInvoiceToPO (Invoice inv -> GroupedPO po) { .... for each (line-items = inv::items) { if (iterator line-items::availability = 'inStock') { // copy the line-item to items under inStock copy(from=iterator line-items::name -> to=po::./inStock/item::name); .... } else { if (iterator line-items:: availability = 'outOfStock') { // define the mapping for buyer copy(from=iterator line-items::name -> to=po::./outofStock/item::name); .... } } } }
Multiple occurrence target datatype members are of a complex datatype that has additional multiple occurrence members. These target members are mapped from source members having a similar structure of nested multiple occurrences.
Target PO
has multiple items that each contain multiple descriptions. PO
is mapped from a similarly-structured source Invoice
that has multiple line-items. Each line-item has a complex datatype member of multiple occurrences called desc
. Each desc
has a lang
member. Figure 14-7 shows this scenario.
Invocation of a datatype transformation map is required within an iterator over the outer multiple occurrence source member. The invoked datatype transformation map also has another iterator over the nested multiple occurrence source member.
copyInvoiceToPO (Invoice inv -> PO po) { .... for each (lineItems = inv::./line-item) { copyLineItemToItem (from=iterator line-items -> to=po::./item); } .... } copyLineItemToItem (lineItem srcLineItem -> item targetItem) { copy (from=srcLineItem::id -> to= targetItem/identifier); copy (from=srcLineItem::qty -> to= targetItem/quantity); copy (from=srcLineItem::cost -> to= targetItem/ cost); for each (descs = srcLineItem::desc) { copyDescToDescription (from=iterator descs -> to=targetItem::description); } } copyDescToDescription (desc srcDesc -> description targetDescription) { copy (from= srcDesc::lang -> to= targetDescription::lang);
This section describes mappings to a single occurrence target member from a multiple occurrence source member that require a way to select the specific source member to be mapped.
A particular multiple occurrence source member is selected based on a condition over the source data and mapped to a single occurrence target member.
Source Invoice
has multiple party
datatype members with a type
value within it indicating the kind of party. The target member has single buyer
and seller
members.
The target member buyer
of target PO
is mapped from the source multiple occurrence party
of Invoice
whose type
is set to purchaser
. Similarly, seller
is mapped from party
with a type of supplier
, as shown in Figure 14-8.
A condition must be defined within the iterator to verify the type of Party
being iterated over for the appropriate mapping.
copyInvoiceToPO (Invoice inv -> PO po) { .... for each (parties = inv::party) { if (iterator parties::type = 'purchaser') { // define the mapping for buyer copy(from=iterator parties::name -> to=po::./buyer/name); .... } else { if (iterator parties::type = 'supplier') // define the mapping for seller copy(from=iterator parties::name -> to=po::./seller/name); .... } } } }
A particular multiple occurrence source member is selected based on its position in the source instance and mapped to a single occurrence target member.
The source Party
contains multiple contact information, but only the first contact information is mapped to the target Party
that contains only single contact information.
Figure 14-9 shows this scenario.
Transformation does not currently provide access to the position of the current item within the iterator. Therefore, you must explicitly define a map variable to keep track of the first item (contact
) in the iteration and copy it to the target.
copy (Party srcParty -> Party targetParty) { // declare a map variable int iteratorPosition ; .... // initialize the map variable copy (from='0' -> to=variable iteratorPosition); // iterate over the contacts for each (contact = srcParty::contact) { if (variable iteratorPosition = '0') { // copy the current contact being iterated to the target contact copy(iterator contact,targetParty::contact); .... } // increment the iterator position add(firstInteger= iteratorPosition, SecondInteger='1' -> to= iteratorPosition); } }
Even though the type definition states that a particular source member has multiple occurrences, the actual instances may not have more than one occurrence. In such a case, you can directly copy this source member without using an iterator.
If the instance has multiple occurrences and you have mapped it without using an iterator, you receive a runtime error. Therefore, the usage of this pattern shown in Figure 14-10 is discouraged.
As with the previous example, the source Party
has member contact information with potential multiple occurrences, but the actual instances always have single occurrences of contact information that is mapped to the target contact.
The single source contact is directly mapped to the target contact without use of iterations.
copy (Party srcParty -> Party targetParty) { copy(srcParty::contact -> targetParty::contact); .... }
The multiple occurrences of a source member are iterated over to perform a cumulative computation (such as summation and average) on the values being iterated over.
Target PO
has a totalAmount
type that is computed by iterating and summing up all the source line totals, as shown in Figure 14-11.
A map variable is declared for invoiceTotal
. Iteration is defined over a source line-item within which each line-total
is added to invoiceTotal
. At the end of the iteration, the invoiceTotal
is copied to the target totalAmount
.
copyInvoiceToPO (Invoice inv -> PO po) { // the map variable for the total int invoiceTotal; ... for each (lineItems = inv::./line-item) { // add line-total to invoice total add(firstInteger=iterator line-items::line-total) , SecondInteger=variable invoiceTotal -> to= variable invoiceTotal); } //copy the computed invoice total to the po totalAmount copy(from=variable invoiceTotal -> to=po:totalAmount); }
This scenario refers to the case where a multiple occurrence complex datatype has further multiple occurrence members. Such nested source members are mapped to a target with only one level of multiple occurrence members; that is, the multiple occurrence target datatype member does not have any further child members with multiple occurrences. Such mappings require the source nested structure to be flattened out by a Cartesian product of the source members.
The source ComputersByVendor
has multiple vendor
types that each have a list of the supported operating system types (identified by OS
in the following example). The target has a flattened list of computer
types with each computer
having the vendor
and OS
.
Sample Source Instance:
<computersByVendor> <vendor> <name>Gateway</name> <OS>Windows</OS> <OS>Linux</OS> </vendor> <vendor> <name>Dell</name> <OS>Windows</OS> <OS>Linux</OS> </vendor> <vendor> <name>Sun</name> <OS>Linux</OS> <OS>Solaris</OS> </vendor> </computersByVendor>
Sample Target Instance:
<computerFlatList> <computer> <Vendor><name>GateWay</name></Vendor> <OS><name>Windows</name></OS> </computer> <computer> <Vendor><name>GateWay</name></Vendor> <OS><name>Linux</name></OS> </computer> <computer> <Vendor><name>Dell</name></Vendor> <OS><name>Windows</name></OS> </computer> <computer> <Vendor><name>Dell</name></Vendor> <OS><name>Linux</name></OS> </computer> <computer> <Vendor><name>Sun</name></Vendor> <OS><name>Linux</name></OS> </computer> <computer> <Vendor><name>Sun</name></Vendor> <OS><name>Solaris</name></OS> </computer> </computerFlatList>
Figure 14-12 shows this scenario.
This mapping requires the definition of an iterator over the vendor
that contains another (nested) iterator over all the operating system (OS
) types of the current vendor
.
A map invoked within the inner iterator can perform the Cartesian product by accessing the current OS
name (from the inner iterator) and the current vendor
name (from the outer iterator) and invoke a datatype transformation map that creates a target computer
with the OS
name and vendor
name.
copyFlatComputerListToComputersByVendor (ComputersByVendor computerByVendor) -> (FlatComputerList flatComputerList) { // iterate over the vendors for each (vendors = computerByVendor::Vendor) { // iterate over the OS's of the current vendor // requires accessing the current item of the // outer iterator to define the items to iterate // over for the inner iterator for each (OSs = iterator[vendors]::OS) { mapComputer (oSName=iterator[OSs]::name, vendorName=iterator[vendors]::name) -> (computer=flatComputerList::computer); } mapComputer (String osName, String vendorName) -> (Computer computer) { copy(from=vendorName) -> (to=computer::vendor/name); copy(from=osName) -> (to=computer::OS/name); }
This section describes advanced nesting scenarios.
This section contains these topics:
See Also:
This section describes mapping of multiple source members to a target based on a condition.
If the total amount is more that $10,000
under the source invoice
, then iterate over the line-items and increase the quantity by 1
for all line-items whose quantity is greater than 50
, as shown in Figure 14-13.
The mapping requires a condition on the element total under the invoice
element. Iterate over the line-item for all the instances where the total amount is greater that $10,000
.
For all elements whose quantity is greater that 50
, increment the line-item by 1
within each line-item.
If( inv::./line-item/total > 10000 ) { int totalQuantity; ... for each (lineItems = invoice::./line-item) { if( iterator line-items::quantity > 50 ) { add(first=iterator line-items::quantity, second=1 -> result= variable totalQuantity); //copy the computed total quantity to the po quantity copy(from=variable totalQuantity -> to=po::.item/quantity); } }
A second condition is evaluated based on a value computed after the first condition evaluation.
If street1
and street2
are not null under Address
, (the first condition), concatenate these two strings. If the concatenated string's length is greater than 8
, do a substring of length 8
.
Figure 14-14 shows this scenario.
This mapping requires a condition to verify that the values of street1
and street2
are not null. If the respective values are not null, concatenate street1
and street2
. The inner condition must check for a length greater than 8
characters. A substring operation is performed if the concatenated string has a length greater than 8
characters.
if ( street1 != null && street2 != null ){concatenetedStreet = concat ( street1 , street2 ); if (concatenetedStreet.size() > 8 ) { resultStreet = subString (concatenetedStreet ,8); copy(from= resultStreet -> to=street); } copy(from= concatenetedStreet -> to=street);}
Although this example uses simple rules within the control flow constructs, domain value map and event header rules can also be used.
This section describes how to design advanced transformation map variable scenarios.
See Also:
|
Map variables can also preserve the results of a computation, enabling it to be used in multiple places.
A source Invoice
has a Seller
, Buyer
, and Distributor
, while a target PO
has a Supplier
, Purchaser
, and Distributor
. They map as shown in Table 14-2.
Invoice | -> | PO |
---|---|---|
|
|
|
|
|
|
|
|
|
All three sources in Invoice
(Seller
, Buyer
, and Distributor
) have an address that has street1
and street2
, while the corresponding addresses in PO
have only one field for street. An Invoice
address is transformed to a PO
address using concatString and other transformation maps.
The Distributor
is usually the same as the Seller
. In these cases, you can use map variables to perform the address computation once and use it in multiple places, as shown in "Mapping Logic":
The mapping logic is as follows:
copyInvoiceToPO (Invoice inv -> PO po) {// the map variable for the address Address poSupplierAddr;concatString(firstString=inv::Seller::./address/street1, secondString=inv::Seller::./address/street2, resultString=variable poSupplierAddr::./street);copyString(from=variable poSupplierAddr, to=po::Supplier::./address);if (inv::Seller::./name = inv::Distributor::./name) {copyString(from=variable poSupplierAddr, to=po::Supplier::./address);}}
This section describes how to design advanced event header rules.
Retrieve the name of the from party using the event header rule getFromParty
. Use the retrieved value to then retrieve the contact information of the party by using the transformation rule getPartyContactInformation
.
This example retrieves the name of the from party from the event header into a map variable FromPartyNameMapVariable
by invoking the event header rule getFromParty
.
It then retrieves the party's Email
by invoking the transformation map getPartyContactInformation
. The global business identifier identified by the data universal numbering system (DUNS) number is also retrieved by invoking the transformation map getPartyContactInformation
.
string FromPartyNameMapVariable; getFromParty (fromParty=FromPartyNameMapVariable); getPartyContactInformation (partyName = FromPartyNameMapVariable, contactInfoType = 'Email' -> contactInfo = Ad_request/HR_Contact_Email); //Note: assuming HR_Contact_Email to be next in //sequence after HR_Contact_ Name getPartyContactInformation (partyName = FromPartyNameMapVariable, partyIdentificationType = 'Contact Name' -> partyIdentification = Ad_request/HR_ Contact_Name)
This section describes how to design advanced domain value maps.
In a domain value map, multiple parties can be associated with the same domain (column). A domain value map example is provided in the hiring process tutorial described in Chapter 7, "Tutorial of an Integration within an Enterprise". Assume you have many local newspapers and many global newspapers. Specifically, The Mercury and The Chronicle are of type Local Newspaper, and The Tribune and The Herald are of type Global Newspaper. Now suppose, the HR App, the local newspapers, and the global newspapers all have different terms for an advertisement. This is shown in Figure 14-15.
To look up the common terminology given the description used by the HR App, you use the following:
HrAdToCommonView[EventTypeMap] (AppEventType HR_Ad -> BusEventType Common_Ad) {lookupBusinessViewDomain (Party=HR_App, inValue=description -> outValue=description) in DVM AdTerminology;}
This section describes the mapping scenarios where the source and target structure have different levels of nesting of datatype members with multiple occurrences.
This section contains these topics:
For nested choices or sequences, inner choices or sequences are modeled as anonymous members. Anonymous members cannot be mapped to any target as they are not present in the instance.
See Also:
"Creating a Complex Datatype" for conceptual details about the choice and sequence model groups |
The phone or e-mail are mapped under the choice member of the Ad_Request
datatype. The choice member cannot be mapped to something else. In addition, any source member cannot be mapped to the target choice member.
The mapping logic is as follows:
copy(from=./[3]/phone -> to=./[3]/phone)
Figure 14-16 shows this scenario.
A member of a datatype with a model group of choice must be checked for existence in the instance.
The street1
and street2
datatype members are under the ADDRESS
datatype whose model group is choice. Therefore, in the instance either street1
or street2
appear, but not both. Before copying street1
or street2
to the target, its existence can be checked in the instance.
if ( Exists ( street1 ) ) {copy( from = street1 -> to = street1);} else if (Exists ( street2) {copy( from = street2 -> to = street2);}
Assume a datatype transformation map takes a complex datatype map parameter. This scenario describes a case where such a map invokes another submap with the entire complex datatype as a map parameter.
When another submap is invoked with the same source or target parameter in a map, no path expression is required. Assume street
is of multiple cardinality within the Addr
complex datatype map parameter. You invoke a submap copyStreet
that iterates over all the street
members in the source and copies them to the target.
The mapping logic is as follows:
copyAddress(AppDatatatype Addr src -> AppDatatype Address target) { copyStreet(src=Addr -> target=Address); }
There can be scenarios where more than one datatype member with the same name is present under a datatype (in the instance, they are known as siblings) and different transformation rules are defined for each. Although they appear under the same parent in the instance, Oracle Application Server ProcessConnect differentiates between them and executes only the rule defined for the respective member.
The Address
datatype's model group is sequence and it has two members with the same name of URL
separated by another mandatory member (Name
). These two members can have different transformation rules. Transformation can correctly determine the association during runtime.
The mapping logic is as follows:
copyAddresses[DatatypeMap](AppDatatype Addr src -> AppDatatype Address target) {copyString(from=Address::./URL[1] -> to=Addr::./URL); copyString(from=Address::./URL[2] -> to=Addr::./contact/URL);}
Figure 14-17 shows this scenario.
This chapter describes both troubleshooting and advanced topics in transformation design. Troubleshooting topics include common user errors (for example, not checking for the existence of an optional datatype member) and limitations on a transformation design (for example, only global map variables are supported). Advanced topics include how to design scenarios with iteration, nesting, and map variables (such as map variables that can preserve the results of a computation).
|
![]() Copyright © 2003 Oracle Corporation. All Rights Reserved. |
|