/// <summary>
            /// Recursively processes an FpML 5-x document to produce a new document
            /// containin the stripped down product infosets.
            /// </summary>
            /// <param name="context">The <see cref="XmlNode"/> to be copied.</param>
            /// <param name="document">The new <see cref="XmlDocument"/> instance.</param>
            /// <param name="parent">The new parent <see cref="XmlNode"/>.</param>
            private void Transcribe(XmlNode context, XmlDocument document, XmlElement parent)
            {
                if (context != null)
                {
                    switch (context.NodeType)
                    {
                    case XmlNodeType.Attribute:
                    {
                        XmlAttribute attribute = (XmlAttribute)context;

                        // Remove unnecessary ID attributes
                        if (XPath.Match(attribute, "businessCenters", "id") ||
                            XPath.Match(attribute, "swapStream", "id") ||
                            XPath.Match(attribute, "swapStream", "resetDates", "id") ||
                            XPath.Match(attribute, "europeanExercise", "id") ||
                            XPath.Match(attribute, "americanExercise", "id") ||
                            XPath.Match(attribute, "bermudaExercise", "id"))
                        {
                            break;
                        }

                        if (XPath.Match(attribute, "currencyScheme") &&
                            attribute.Value.Equals("http://www.fpml.org/ext/iso4217"))
                        {
                            break;
                        }

                        parent.SetAttribute(attribute.Name, attribute.Value);
                        break;
                    }

                    case XmlNodeType.Element:
                    {
                        XmlElement element = context as XmlElement;

                        // Strip an existing taxonomy data and identification
                        if (XPath.Match(element, "productType") ||
                            XPath.Match(element, "assetClass") ||
                            XPath.Match(element, "productid"))
                        {
                            break;
                        }

                        // Remove party specific references
                        if (XPath.Match(element, "buyerPartyReference") ||
                            XPath.Match(element, "buyerAccountReference") ||
                            XPath.Match(element, "sellerPartyReference") ||
                            XPath.Match(element, "sellerAccountReference") ||
                            XPath.Match(element, "payerPartyReference") ||
                            XPath.Match(element, "payerAccountReference") ||
                            XPath.Match(element, "receiverPartyReference") ||
                            XPath.Match(element, "receiverAccountReference") ||
                            XPath.Match(element, "partyReference") ||
                            XPath.Match(element, "calculationAgentPartyReference"))
                        {
                            break;
                        }

                        // Remove monetary amounts
                        if (XPath.Match(element, "notional", "amount") ||
                            XPath.Match(element, "paymentAmount", "amount") ||
                            XPath.Match(element, "stubAmount", "amount"))
                        {
                            break;
                        }

                        // Remove calculation period references
                        if (XPath.Match(element, "paymentDates", "calculationPeriodDatesReference") ||
                            XPath.Match(element, "resetDates", "calculationPeriodDatesReference") ||
                            XPath.Match(element, "stubCalculationPeriodAmount", "calculationPeriodDatesReference"))
                        {
                            break;
                        }

                        // Remove reset dates references
                        if (XPath.Match(element, "fixingDates", "dateRelativeTo") ||
                            XPath.Match(element, "fixingDateOffset", "dateRelativeTo") ||
                            XPath.Match(element, "relativeDate", "dateRelativeTo"))
                        {
                            break;
                        }

                        // Remove dates
                        if (XPath.Match(element, "paymentDate", "unadjustedDate") ||
                            XPath.Match(element, "adjustableDate", "unadjustedDate") ||
                            XPath.Match(element, "paymentDates", "firstPaymentDate") ||
                            XPath.Match(element, "fra", "adjustedEffectiveDate") ||
                            XPath.Match(element, "fra", "adjustedTerminationDate"))
                        {
                            break;
                        }

                        // Replace businessCentersReference with a copy of the target
                        if (XPath.Match(element, "businessCentersReference"))
                        {
                            Transcribe(element.OwnerDocument.GetElementById(element.GetAttribute("href")), document, parent);
                            break;
                        }

                        // Strip schedules
                        if (XPath.Match(element, "notionalStepSchedule", "initialValue") ||
                            XPath.Match(element, "fixedRateSchedule", "initialValue") ||
                            XPath.Match(element, "spreadSchedule", "initialValue") ||
                            XPath.Match(element, "capRateSchedule", "initialValue") ||
                            XPath.Match(element, "floorRateSchedule", "initialValue"))
                        {
                            break;
                        }

                        // Keep an indication of step presence but remove values
                        if (XPath.Match(element, "step"))
                        {
                            XmlElement prevElement = DOM.GetPreviousSibling(element);

                            if ((prevElement != null) && XPath.Match(prevElement, "step"))
                            {
                                break;
                            }
                        }
                        if (XPath.Match(element, "step", "stepDate") ||
                            XPath.Match(element, "step", "stepValue"))
                        {
                            break;
                        }

                        // Remove rates
                        if (XPath.Match(element, "initialStub", "stubRate") ||
                            XPath.Match(element, "finalStub", "stubRate") ||
                            XPath.Match(element, "fra", "fixedRate"))
                        {
                            break;
                        }

                        // Remove other odd elements
                        if (XPath.Match(element, "fra", "calculationPeriodNumberOfDays"))
                        {
                            break;
                        }

                        // Clone the original element
                        XmlElement clone = document.CreateElement(element.LocalName, R1_0.NamespaceUri);
                        parent.AppendChild(clone);

                        // Keep an indication of cashflows but remove values
                        if (XPath.Match(element, "swapStream", "cashflows"))
                        {
                            break;
                        }

                        // Normalise swaps
                        if (XPath.Match(element, "swap"))
                        {
                            OrderSwapStreams(element, document, clone);

                            Transcribe(element ["earlyTerminationProvision"], document, clone);
                            Transcribe(element ["cancelableProvision"], document, clone);
                            Transcribe(element ["extendibleProvision"], document, clone);
                            break;
                        }

                        // Make swap streams relative
                        if (XPath.Match(element, "swapStream", "calculationPeriodDates"))
                        {
                            XmlElement effectiveDate;
                            XmlElement terminationDate;

                            if ((effectiveDate = XPath.Path(element, "firstRegularPeriodDate", "unadjustedDate")) == null)
                            {
                                effectiveDate = XPath.Path(element, "effectiveDate", "unadjustedDate");
                            }

                            if ((terminationDate = XPath.Path(element, "lastRegularPeriodDate", "unadjustedDate")) == null)
                            {
                                terminationDate = XPath.Path(element, "terminationDate", "unadjustedDate");
                            }

                            // Replace absolute dates with tenors
                            if ((effectiveDate != null) && (terminationDate != null))
                            {
                                XmlElement tenor = document.CreateElement("relativeTerminationDate", R1_0.NamespaceUri);
                                clone.AppendChild(tenor);

                                DeriveTenor(Types.ToDate(effectiveDate), Types.ToDate(terminationDate), document, tenor);
                                Transcribe(element ["dayType"], document, tenor);

                                Transcribe(element ["calculationPeriodDatesAdjustments"], document, clone);
                                Transcribe(element ["stubPeriodType"], document, clone);
                                Transcribe(element ["calculationPeriodFrequency"], document, clone);
                                break;
                            }
                        }

                        // Make FRAs relative
                        if (XPath.Match(element, "fra"))
                        {
                            XmlElement effectiveDate   = XPath.Path(element, "adjustedEffectiveDate");
                            XmlElement terminationDate = XPath.Path(element, "adjustedTerminationDate");

                            if ((effectiveDate != null) && (terminationDate != null))
                            {
                                XmlElement tenor = document.CreateElement("relativeTerminationDate", R1_0.NamespaceUri);
                                clone.AppendChild(tenor);

                                DeriveTenor(Types.ToDate(effectiveDate), Types.ToDate(terminationDate), document, tenor);
                            }
                        }

                        // Derive Expiration date tenor
                        if (XPath.Match(element, "europeanExercise", "expirationDate"))
                        {
                            XmlElement tradeDate  = XPath.Path(FindTrade(element), "tradeHeader", "tradeDate");
                            XmlElement expiryDate = XPath.Path(element, "adjustableDate", "unadjustedDate");

                            if ((tradeDate != null) && (expiryDate != null))
                            {
                                XmlElement tenor = document.CreateElement("relativeExpirationDate", R1_0.NamespaceUri);
                                clone.AppendChild(tenor);

                                DeriveTenor(Types.ToDate(tradeDate), Types.ToDate(expiryDate), document, tenor);

                                XmlElement adjustments = XPath.Path(element, "adjustableDate", "dateAdjustments");
                                if (adjustments != null)
                                {
                                    Transcribe(adjustments, document, clone);
                                }
                                break;
                            }
                        }

                        // Transfer and update attributes
                        foreach (XmlAttribute attr in element.Attributes)
                        {
                            Transcribe(attr, document, clone);
                        }

                        // Recursively copy the child node across
                        foreach (XmlNode node in element.ChildNodes)
                        {
                            Transcribe(node, document, clone);
                        }

                        break;
                    }

                    case XmlNodeType.Text:
                    {
                        parent.AppendChild(document.CreateTextNode(((XmlText)context).Value));
                        break;
                    }
                    }
                }
            }