All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opengamma.strata.loader.fpml.FpmlDocumentParser Maven / Gradle / Ivy

There is a newer version: 2.12.46
Show newest version
/*
 * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.strata.loader.fpml;

import static com.opengamma.strata.collect.Guavate.toImmutableSet;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteSource;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.collect.io.XmlElement;
import com.opengamma.strata.collect.io.XmlFile;
import com.opengamma.strata.collect.named.ExtendedEnum;
import com.opengamma.strata.product.Trade;

/**
 * Loader of trade data in FpML format.
 * 

* This handles the subset of FpML necessary to populate the trade model. * The standard parsers accept FpML v5.8, which is often the same as earlier versions. *

* The trade parsers implement {@link FpmlParserPlugin} and are pluggable using * the {@code FpmlParserPlugin.ini} configuration file. */ public final class FpmlDocumentParser { // Notes: Streaming trades directly from the file is difficult due to the // need to parse the party element at the root, which is after the trades /** * The lookup of trade parsers. */ static final ExtendedEnum ENUM_LOOKUP = ExtendedEnum.of(FpmlParserPlugin.class); /** * The selector used to find "our" party within the set of parties in the FpML document. */ private final FpmlPartySelector ourPartySelector; /** * The trade info parser. */ private final FpmlTradeInfoParserPlugin tradeInfoParser; /** * The trade parsers. */ private final Map tradeParsers; /** * The reference data. */ private final ReferenceData refData; /** * Flag indicating whether to be strict about the presence of unsupported elements. */ private final boolean strictValidation; //------------------------------------------------------------------------- /** * Obtains an instance of the parser, based on the specified selector. *

* The FpML parser has a number of plugin points that can be controlled: *

    *
  • the {@linkplain FpmlPartySelector party selector} *
  • the {@linkplain FpmlTradeInfoParserPlugin trade info parser} *
  • the {@linkplain FpmlParserPlugin trade parsers} *
  • the {@linkplain ReferenceData reference data} *
* This method uses the {@linkplain FpmlTradeInfoParserPlugin#standard() standard} * trade info parser, the trade parsers registered in {@link FpmlParserPlugin} * configuration and the {@linkplain ReferenceData#standard() standard} reference data. * * @param ourPartySelector the selector used to find "our" party within the set of parties in the FpML document * @return the document parser */ public static FpmlDocumentParser of(FpmlPartySelector ourPartySelector) { return of(ourPartySelector, FpmlTradeInfoParserPlugin.standard()); } /** * Obtains an instance of the parser, based on the specified selector and trade info plugin. *

* The FpML parser has a number of plugin points that can be controlled: *

    *
  • the {@linkplain FpmlPartySelector party selector} *
  • the {@linkplain FpmlTradeInfoParserPlugin trade info parser} *
  • the {@linkplain FpmlParserPlugin trade parsers} *
  • the {@linkplain ReferenceData reference data} *
* This method uses the trade parsers registered in {@link FpmlParserPlugin} configuration * and the {@linkplain ReferenceData#standard() standard} reference data. * * @param ourPartySelector the selector used to find "our" party within the set of parties in the FpML document * @param tradeInfoParser the trade info parser * @return the document parser */ public static FpmlDocumentParser of( FpmlPartySelector ourPartySelector, FpmlTradeInfoParserPlugin tradeInfoParser) { return of(ourPartySelector, tradeInfoParser, FpmlParserPlugin.extendedEnum().lookupAllNormalized()); } /** * Obtains an instance of the parser, based on the specified selector, trade info plugin and reference data. *

* The FpML parser has a number of plugin points that can be controlled: *

    *
  • the {@linkplain FpmlPartySelector party selector} *
  • the {@linkplain FpmlTradeInfoParserPlugin trade info parser} *
  • the {@linkplain FpmlParserPlugin trade parsers} *
  • the {@linkplain ReferenceData reference data} *
* This method uses the trade parsers registered in {@link FpmlParserPlugin} configuration. * * @param ourPartySelector the selector used to find "our" party within the set of parties in the FpML document * @param tradeInfoParser the trade info parser * @param refData the reference data to use * @return the document parser */ public static FpmlDocumentParser of( FpmlPartySelector ourPartySelector, FpmlTradeInfoParserPlugin tradeInfoParser, ReferenceData refData) { return of(ourPartySelector, tradeInfoParser, FpmlParserPlugin.extendedEnum().lookupAllNormalized(), refData); } /** * Obtains an instance of the parser, based on the specified selector and plugins. *

* The FpML parser has a number of plugin points that can be controlled: *

    *
  • the {@linkplain FpmlPartySelector party selector} *
  • the {@linkplain FpmlTradeInfoParserPlugin trade info parser} *
  • the {@linkplain FpmlParserPlugin trade parsers} *
  • the {@linkplain ReferenceData reference data} *
* This method uses the {@linkplain ReferenceData#standard() standard} reference data. * * @param ourPartySelector the selector used to find "our" party within the set of parties in the FpML document * @param tradeInfoParser the trade info parser * @param tradeParsers the map of trade parsers, keyed by the FpML element name * @return the document parser */ public static FpmlDocumentParser of( FpmlPartySelector ourPartySelector, FpmlTradeInfoParserPlugin tradeInfoParser, Map tradeParsers) { return of(ourPartySelector, tradeInfoParser, tradeParsers, ReferenceData.standard()); } /** * Obtains an instance of the parser, based on the specified selector and plugins. *

* The FpML parser has a number of plugin points that can be controlled: *

    *
  • the {@linkplain FpmlPartySelector party selector} *
  • the {@linkplain FpmlTradeInfoParserPlugin trade info parser} *
  • the {@linkplain FpmlParserPlugin trade parsers} *
  • the {@linkplain ReferenceData reference data} *
* * @param ourPartySelector the selector used to find "our" party within the set of parties in the FpML document * @param tradeInfoParser the trade info parser * @param tradeParsers the map of trade parsers, keyed by the FpML element name * @param refData the reference data to use * @return the document parser */ public static FpmlDocumentParser of( FpmlPartySelector ourPartySelector, FpmlTradeInfoParserPlugin tradeInfoParser, Map tradeParsers, ReferenceData refData) { return new FpmlDocumentParser(ourPartySelector, tradeInfoParser, tradeParsers, refData, true); } //------------------------------------------------------------------------- /** * Creates an instance, based on the specified element. * * @param ourPartySelector the selector used to find "our" party within the set of parties in the FpML document * @param tradeInfoParser the trade info parser * @param tradeParsers the map of trade parsers, keyed by the FpML element name * @param strictValidation flag indicating whether to be strict when validating which elements * are present in the FpML document */ private FpmlDocumentParser( FpmlPartySelector ourPartySelector, FpmlTradeInfoParserPlugin tradeInfoParser, Map tradeParsers, ReferenceData refData, boolean strictValidation) { this.ourPartySelector = ourPartySelector; this.tradeInfoParser = tradeInfoParser; this.tradeParsers = tradeParsers; this.refData = refData; this.strictValidation = strictValidation; } //------------------------------------------------------------------------- /** * Obtains a 'lenient' version of this parser instance. *

* In 'lenient' mode any FpML elements unsupported in Strata will be silently ignored (rather than resulting in errors). * * @return the lenient document parser */ public FpmlDocumentParser withLenientMode() { return new FpmlDocumentParser(ourPartySelector, tradeInfoParser, tradeParsers, refData, false); } /** * Basic check to see if the source can probably be parsed as FpML. *

* This checks the first part of the byte source to see if it appears to be FpML. * This is determined by looking for "fpml" (case insensitive) within the first 2000 characters. *

* A more thorough check would involve parsing the XML, which is relatively slow. * If you want to perform the more thorough check benchmarking indicates you might * as well just parse the whole document. * * @param source the source to check * @return true if the source appears to be FpML * @throws UncheckedIOException if an IO error occurred */ public boolean isKnownFormat(ByteSource source) { try { byte[] array = source.slice(0, 2000).read(); for (int i = 0; i < array.length - 7; i++) { // the `| 32` converts upper case letters to lower case ones if ((array[i] | 32) == 'f') { // UTF-8 if ((array[i + 1] | 32) == 'p' && (array[i + 2] | 32) == 'm' && (array[i + 3] | 32) == 'l') { return true; } // UTF-16 if (array[i + 1] == '\000' && (array[i + 2] | 32) == 'p' && array[i + 3] == '\000' && (array[i + 4] | 32) == 'm' && array[i + 5] == '\000' && (array[i + 6] | 32) == 'l') { return true; } } } return false; } catch (IOException ex) { throw new UncheckedIOException(ex); } } /** * Parses FpML from the specified source, extracting the trades. *

* This parses the specified byte source which must be an XML document. *

* Sometimes, the FpML document is embedded in a non-FpML wrapper. * This method will intelligently find the FpML document at the root, within any children of * the root, or within any grand-children of the root. * The FpML root element is the one that contains both {@code } and {@code }. * * @param source the source of the FpML XML document * @return the parsed trades * @throws RuntimeException if a parse error occurred */ public List parseTrades(ByteSource source) { XmlFile xmlFile = XmlFile.of(source, FpmlDocument.ID); return parseTrades(xmlFile); } /** * Parses FpML from the specified XML document, extracting the trades. *

* This parses the specified XmlFile. *

* Sometimes, the FpML document is embedded in a non-FpML wrapper. * This method will intelligently find the FpML document at the root, within any children of * the root, or within any grand-children of the root. * The FpML root element is the one that contains both {@code } and {@code }. * * @param xmlFile the FpML XML document * @return the parsed trades * @throws RuntimeException if a parse error occurred */ public List parseTrades(XmlFile xmlFile) { XmlElement root = findFpmlRoot(xmlFile.getRoot()); if (root == null) { throw new FpmlParseException("Unable to find FpML root element"); } return parseTrades(root, xmlFile.getReferences()); } // intelligently finds the FpML root element private static XmlElement findFpmlRoot(XmlElement root) { XmlElement fpmlRoot = getFpmlRoot(root); if (fpmlRoot != null) { return fpmlRoot; } // try children of root element for (XmlElement el : root.getChildren()) { fpmlRoot = getFpmlRoot(el); if (fpmlRoot != null) { return fpmlRoot; } } // try grandchildren of root element for (XmlElement el1 : root.getChildren()) { for (XmlElement el2 : el1.getChildren()) { fpmlRoot = getFpmlRoot(el2); if (fpmlRoot != null) { return fpmlRoot; } } } return null; } // simple check to see if this is an FpML root private static XmlElement getFpmlRoot(XmlElement el) { if (el.getChildren("party").size() > 0) { // party and trade are siblings (the common case) if (el.getChildren("trade").size() > 0) { return el; } // trade is within a child alongside party (the unusual case, within clearingStatus/clearingStatusItem) for (XmlElement child : el.getChildren()) { if (child.getChildren("trade").size() > 0) { List fakeChildren = new ArrayList<>(); fakeChildren.addAll(el.getChildren("party")); fakeChildren.addAll(child.getChildren("trade")); XmlElement fakeRoot = XmlElement.ofChildren(el.getName(), el.getAttributes(), fakeChildren); return fakeRoot; } } // trade is within a grandchild alongside party (the unusual case, within clearingConfirmed/clearing/cleared) for (XmlElement child : el.getChildren()) { for (XmlElement grandchild : child.getChildren()) { if (grandchild.getChildren("trade").size() > 0) { List fakeChildren = new ArrayList<>(); fakeChildren.addAll(el.getChildren("party")); fakeChildren.addAll(grandchild.getChildren("trade")); XmlElement fakeRoot = XmlElement.ofChildren(el.getName(), el.getAttributes(), fakeChildren); return fakeRoot; } } } } return null; } //------------------------------------------------------------------------- /** * Parses the FpML document extracting the trades. *

* This parses the specified FpML root element, using the map of references. * The FpML specification uses references to link one part of the XML to another. * For example, if one part of the XML has {@code }, the references * map will contain an entry mapping "fooId" to the parsed element {@code }. * * @param fpmlRootEl the source of the FpML XML document * @param references the map of id/href to referenced element * @return the parsed trades * @throws RuntimeException if a parse error occurred */ public List parseTrades( XmlElement fpmlRootEl, Map references) { FpmlDocument document = new FpmlDocument(fpmlRootEl, references, ourPartySelector, tradeInfoParser, refData, strictValidation); List tradeEls = document.getFpmlRoot().getChildren("trade"); ImmutableList.Builder builder = ImmutableList.builder(); for (XmlElement tradeEl : tradeEls) { builder.add(parseTrade(document, tradeEl)); } return builder.build(); } // parses one trade element private Trade parseTrade(FpmlDocument document, XmlElement tradeEl) { // find which trade type it is by comparing children to known parsers for (Entry entry : tradeParsers.entrySet()) { Optional productOptEl = tradeEl.findChild(entry.getKey()); if (productOptEl.isPresent()) { return entry.getValue().parseTrade(document, tradeEl); } } // failed to find a known trade type ImmutableSet childNames = tradeEl.getChildren().stream().map(XmlElement::getName).collect(toImmutableSet()); throw new FpmlParseException("Unknown product type '{value}'", childNames); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy