-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into website-changes2
- Loading branch information
Showing
145 changed files
with
6,413 additions
and
2,865 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,14 @@ | ||
|
||
# _Product Model - Asset Refactoring in AssetCriteria_ | ||
|
||
_Background_ | ||
|
||
The Asset Refactoring initiative (see https://github.com/finos/common-domain-model/issues/2805) is seeking to improve the Product Model to address some long-standing issues and to ensure the continued extensibility to additional financial products and markets. A proposal has been agreed - through a cross-industry Task Force - to implement this remodelling in the CDM. | ||
|
||
This release includes some additional functionality (following three planned major tranches of work in CDM 6 to implement the refactored model). | ||
|
||
_What is being released?_ | ||
|
||
AssetCriteria: | ||
- The attribute `assetIdentifier` has been refactored to model an actual asset, specified using the `Asset` choice data type, rather than just an identifier. The attribute name has also been updated to `specificAssets` to make it clear that it is a list of specific assets, all of whom are eligible to be pledged as collateral. The condition on the data type has been updated too. | ||
|
||
ListingType: | ||
- The cardinality of the three attributes in the data type `ListingType` (which is used in `AssetCriteria`) has been changed to none-to-many (rather than none or one); the attributes are `exchange`, `sector`, `index`. Without this, it would be only possible to select one of the values. | ||
|
||
_Review directions_ | ||
|
||
The changes can be reviewed in PR: [#3228](https://github.com/finos/common-domain-model/pull/3228) | ||
|
||
_Backward-incompatible changes_ | ||
|
||
This release contains changes that are not backward-compatible: | ||
- All references to the attribute `assetIdentifier` on `AssetCriteria` need to be updated as referenced; the new attribute is `asset`. | ||
|
||
# _Mapping Update - InterestRateForwardDebtPriceMappingProcessor updated to handle 'Percentage' quoteUnits_ | ||
# _CDM Model - CapacityUnit Enum_ | ||
|
||
_Background_ | ||
|
||
The price of bond forwards is captured as a monetary value whereas it should be a decimal/percentage. Even if the value in FpML was 'Percentage', the CDM representation value did not accurately represent this, causing misinterpretations. | ||
In has been seen that in the ExternalUnitOfMeasure1Code from the 2Q2024 ISO External CodeSets v1, the unity Joule is supported in the Enum. However, in CDM this is not the case, as it does not appear anywhere in the CapacityUnitEnum. Therefore, the Joule unit of measure will be added to the CapacityUnitEnum for completeness and to align with 2Q2024 ISO External CodeSets v1, for versions 5 and 6 of CDM. | ||
|
||
_What is being released?_ | ||
|
||
- An update to the **InterestRateForwardDebtPriceMappingProcessor** code to fix the described issue. This change, would correct the interpretation by dividing the current monetary value by 100, when the *quoteUnits* corresponds to the XML value '*Percentage*'. | ||
- The **bond-fwd-generic-ex01.xml** and **bond-fwd-generic-ex02.xml** samples have been updated as the files were using the value 'Percent' but the correct value according to the enum should be 'Percentage' | ||
|
||
|
||
_Review directions_ | ||
- Updated `CapacityUnitEnum` in cdm.base.math | ||
|
||
In Rosetta, select the Textual Browser and inspect each of the changes identified above. | ||
_Enumerations_ | ||
|
||
The changes can be reviewed in PR: [#3242](https://github.com/finos/common-domain-model/pull/3242) | ||
- Updated `CapacityUnitEnum` by adding 'J' to support Joule unit | ||
|
||
The changes can be reviewed in PR: [#3198](https://github.com/finos/common-domain-model/pull/3198) |
200 changes: 200 additions & 0 deletions
200
...tta-source/src/main/java/cdm/event/common/processor/RelatedPartyRoleMappingProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
package cdm.event.common.processor; | ||
|
||
import cdm.base.staticdata.party.PartyRole; | ||
import cdm.base.staticdata.party.PartyRoleEnum; | ||
import cdm.base.staticdata.party.metafields.ReferenceWithMetaParty; | ||
import cdm.event.common.Trade; | ||
import com.regnosys.rosetta.common.translation.Mapping; | ||
import com.regnosys.rosetta.common.translation.MappingContext; | ||
import com.regnosys.rosetta.common.translation.MappingProcessor; | ||
import com.regnosys.rosetta.common.translation.Path; | ||
import com.rosetta.model.lib.RosettaModelObjectBuilder; | ||
import com.rosetta.model.lib.path.RosettaPath; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
import java.util.stream.Collectors; | ||
import java.util.regex.Pattern; | ||
import java.util.regex.Matcher; | ||
|
||
import static com.regnosys.rosetta.common.translation.MappingProcessorUtils.subPath; | ||
import static com.regnosys.rosetta.common.translation.MappingProcessorUtils.updateMappingSuccess; | ||
import static java.util.stream.Collectors.collectingAndThen; | ||
import static java.util.stream.Collectors.toList; | ||
|
||
/** | ||
* Created by Tradeheader, SL | ||
* | ||
* @author gopazoTH | ||
* @since 18/09/2024 | ||
*/ | ||
|
||
@SuppressWarnings("unused") | ||
public class RelatedPartyRoleMappingProcessor extends MappingProcessor { | ||
|
||
private static final Logger LOGGER = LoggerFactory.getLogger(RelatedPartyRoleMappingProcessor.class); | ||
|
||
public RelatedPartyRoleMappingProcessor(RosettaPath modelPath, List<Path> synonymPaths, MappingContext context) { | ||
super(modelPath, synonymPaths, context); | ||
} | ||
|
||
@Override | ||
public void map(Path synonymPath, List<? extends RosettaModelObjectBuilder> builder, RosettaModelObjectBuilder parent) { | ||
|
||
Collection<PartyRoleMapping> partyRoleMappings = getMappings().stream() | ||
// find all partyTradeInformation.relatedParty mappings | ||
.filter(m -> synonymPath.getParent().nameStartMatches(m.getXmlPath())) | ||
// group by relatedParty path index | ||
.collect(Collectors.groupingBy(this::getKey, collectingAndThen(toList(), this::toPartyRoleMapping))) | ||
.values(); | ||
|
||
partyRoleMappings.forEach(m -> { | ||
Trade.TradeBuilder tradeBuilder = (Trade.TradeBuilder) parent; | ||
|
||
// Check if m.role is non-null before calling fromDisplayName | ||
if (m != null && m.role != null) { | ||
try { | ||
// Normalize the XML value to match the enum constant (convert to uppercase and replace spaces/underscores) | ||
PartyRoleEnum roleEnum = PartyRoleEnum.fromDisplayName(m.role); | ||
|
||
// Create the PartyRoleBuilder and add the necessary values | ||
PartyRole.PartyRoleBuilder partyRoleBuilder = PartyRole.builder() | ||
.setRole(roleEnum); | ||
|
||
// Check if m.partyReference is non-null before setting it | ||
if (m.partyReference != null) { | ||
partyRoleBuilder.setPartyReference(ReferenceWithMetaParty.builder() | ||
.setExternalReference(m.partyReference)); | ||
} | ||
|
||
// Check if m.ownershipPartyReference is non-null before setting it | ||
if (m.ownershipPartyReference != null) { | ||
partyRoleBuilder.setOwnershipPartyReference(ReferenceWithMetaParty.builder() | ||
.setExternalReference(m.ownershipPartyReference)); | ||
} | ||
|
||
// Add the PartyRole to the trade | ||
tradeBuilder.addPartyRole(partyRoleBuilder); | ||
|
||
// Update mappings if they are non-null | ||
if (m.partyReferenceMapping != null && m.partyReferenceModelPath != null) { | ||
updateMappingSuccess(m.partyReferenceMapping, m.partyReferenceModelPath); | ||
} | ||
if (m.roleMapping != null && m.roleModelPath != null) { | ||
updateMappingSuccess(m.roleMapping, m.roleModelPath); | ||
} | ||
} | ||
catch (IllegalArgumentException e) { | ||
// If the value is not a valid enum constant, do nothing and skip this iteration | ||
LOGGER.warn("Invalid PartyRoleEnum: " + m.role, e); | ||
|
||
} | ||
} | ||
|
||
|
||
}); | ||
} | ||
|
||
/** | ||
* Group by path index, e.g. collect all mappings under relatedParty(index) | ||
* (nonpublicExecutionReport.trade.tradeHeader.partyTradeInformation(0).relatedParty(10)) | ||
*/ | ||
private String getKey(Mapping m) { | ||
int partyTradeInformationIndex = getPathIndex(m, "partyTradeInformation"); | ||
int relatedPartyIndex = getPathIndex(m, "relatedParty"); | ||
return partyTradeInformationIndex + "-" + relatedPartyIndex; | ||
} | ||
|
||
private int getPathIndex(Mapping m, String elementName) { | ||
return subPath(elementName, m.getXmlPath()) | ||
.map(Path::getLastElement) | ||
.map(Path.PathElement::forceGetIndex) | ||
.orElse(-1); | ||
} | ||
|
||
private PartyRoleMapping toPartyRoleMapping(List<Mapping> mappings) { | ||
try { | ||
if (!mappings.isEmpty()) { | ||
// Get the xmlPath from the first element in the mappings list | ||
String xmlPath = mappings.get(0).getXmlPath().toString(); | ||
|
||
// Extract the index value from partyTradeInformation(x) in the xmlPath | ||
Matcher matcher = Pattern.compile(".*partyTradeInformation\\((\\d+)\\).*").matcher(xmlPath); | ||
|
||
if (matcher.find()) { | ||
String index = matcher.group(1); // This captures the index (x) from partyTradeInformation(x) | ||
|
||
// Dynamically build the pattern using the extracted index | ||
Pattern dynamicPattern = Pattern.compile(".*partyTradeInformation\\(" + index + "\\)\\.partyReference.*"); | ||
|
||
// Apply the dynamic pattern to filter mappings | ||
Mapping ownershipPartyReferenceMapping = getMapping( | ||
getMappings().stream() | ||
.filter(m -> m.getXmlValue() != null && dynamicPattern.matcher(m.getXmlPath().toString()).matches()) | ||
.collect(Collectors.toList()), | ||
Path.parse("partyReference.href") | ||
); | ||
|
||
// Proceed with your existing mapping logic | ||
RosettaPath ownershipPartyReferenceModelPath = getModelPath().newSubPath("ownershipPartyReference").newSubPath("externalReference"); | ||
Mapping partyReferenceMapping = getMapping(mappings.stream().filter(m -> m.getXmlValue() != null).collect(Collectors.toList()), Path.parse("partyReference.href")); | ||
RosettaPath partyReferenceModelPath = getModelPath().newSubPath("partyReference").newSubPath("externalReference"); | ||
Mapping roleMapping = getMapping(mappings.stream().filter(m -> m.getXmlValue() != null).collect(Collectors.toList()), Path.parse("role")); | ||
RosettaPath roleModelPath = getModelPath().newSubPath("role"); | ||
|
||
// Build and return the PartyRoleMapping if the mappings are valid | ||
if (partyReferenceMapping != null && partyReferenceMapping.getXmlValue() != null | ||
&& roleMapping != null && roleMapping.getXmlValue() != null) { return new PartyRoleMapping( | ||
String.valueOf(ownershipPartyReferenceMapping.getXmlValue()), | ||
ownershipPartyReferenceMapping, | ||
ownershipPartyReferenceModelPath, | ||
String.valueOf(partyReferenceMapping.getXmlValue()), | ||
partyReferenceMapping, | ||
partyReferenceModelPath, | ||
String.valueOf(roleMapping.getXmlValue()), | ||
roleMapping, | ||
roleModelPath | ||
); | ||
} | ||
} | ||
} | ||
} catch (Exception e) { | ||
LOGGER.error("Failed to build party role mapping from mappings {}", mappings, e); | ||
} | ||
return null; | ||
} | ||
|
||
|
||
private Mapping getMapping(List<Mapping> mappings, Path endsWith) { | ||
return mappings.stream().filter(m -> m.getXmlPath().endsWith(endsWith)).findFirst().orElse(null); | ||
} | ||
|
||
private static class PartyRoleMapping { | ||
|
||
private final String ownershipPartyReference; | ||
private final Mapping ownershipPartyReferenceMapping; | ||
private final RosettaPath ownershipPartyReferenceModelPath; | ||
private final String partyReference; | ||
private final Mapping partyReferenceMapping; | ||
private final RosettaPath partyReferenceModelPath; | ||
private final String role; | ||
private final Mapping roleMapping; | ||
private final RosettaPath roleModelPath; | ||
|
||
public PartyRoleMapping(String ownershipPartyReference, Mapping ownershipPartyReferenceMapping, RosettaPath ownershipPartyReferenceModelPath,String partyReference, Mapping partyReferenceMapping, RosettaPath partyReferenceModelPath, String role, Mapping roleMapping, RosettaPath roleModelPath) { | ||
this.ownershipPartyReference = ownershipPartyReference; | ||
this.ownershipPartyReferenceMapping = ownershipPartyReferenceMapping; | ||
this.ownershipPartyReferenceModelPath = ownershipPartyReferenceModelPath; | ||
this.partyReference = partyReference; | ||
this.partyReferenceMapping = partyReferenceMapping; | ||
this.partyReferenceModelPath = partyReferenceModelPath; | ||
this.role = role; | ||
this.roleMapping = roleMapping; | ||
this.roleModelPath = roleModelPath; | ||
} | ||
} | ||
} |
Oops, something went wrong.