/// <summary>
        /// Write a primary measure observation
        /// </summary>
        /// <param name="row">
        /// The map between components and their values 
        /// </param>
        /// <param name="info">
        /// The current Data Retrieval state 
        /// </param>
        /// <returns>
        /// The number of observations stored. 
        /// </returns>
        protected override int WriteObservation(MappedXsValues row, DataRetrievalInfoXS info)
        {
            if (row.MeasureDimensionValue == null)
            {
                throw new InvalidOperationException("MappedXsValues.MeasureDimension is null");
            }

            string tag;
            if (info.XSMeasureCodeToConcept.TryGetValue(row.MeasureDimensionValue.Value, out tag))
            {
                return WriteObservation(row, tag, row.PrimaryMeasureValue.Value, info);
            }

            throw new InvalidOperationException(
                string.Format(CultureInfo.InvariantCulture, "Unknown measure code {0}", row.MeasureDimensionValue.Value));
        }
        /// <summary>
        /// Conditionally write the dataset values.
        /// </summary>
        /// <param name="row">
        /// The map between components and their values 
        /// </param>
        /// <param name="info">
        /// The current Data Retrieval state 
        /// </param>
        protected static void WriteDataSet(MappedXsValues row, DataRetrievalInfoXS info)
        {
            if (row.StartedDataSet)
            {
                return;
            }

            foreach (var dataSetLevelDimension in row.DataSetLevelDimensionValues)
            {
                info.XSWriter.WriteDataSetKeyValue(dataSetLevelDimension.Key.Id, dataSetLevelDimension.Value);
            }

            WriteAttributes(row.DataSetLevelAttributeValues, info);

            row.StartedDataSet = true;
        }
 /// <summary>
 /// Write a primary measure observation
 /// </summary>
 /// <param name="row">
 /// The map between components and their values 
 /// </param>
 /// <param name="info">
 /// The current Data Retrieval state 
 /// </param>
 /// <returns>
 /// The number of observations stored. 
 /// </returns>
 protected abstract int WriteObservation(MappedXsValues row, DataRetrievalInfoXS info);
        /// <summary>
        /// Write the CrossSectional Group with the dimensions and attributes found in <paramref name="row"/>
        /// </summary>
        /// <param name="row">
        /// The map between components and their values 
        /// </param>
        /// <param name="info">
        /// The current Data Retrieval state 
        /// </param>
        protected static void WriteSection(MappedXsValues row, DataRetrievalInfoXS info)
        {
            info.XSWriter.StartSection();

            foreach (var dimension in row.SectionLevelDimensionValues)
            {
                info.XSWriter.WriteSectionKeyValue(dimension.Key.Id, dimension.Value);
            }

            // write observation attributes
            WriteAttributes(row.SectionAttributeValues, info);
        }
        /// <summary>
        /// Write observation element. The time, observation and attribute values are in <paramref name="row"/>
        /// </summary>
        /// <param name="row">
        /// The map between components and their values 
        /// </param>
        /// <param name="tag">
        /// The element tag. The Primary or XS measure concept ref. 
        /// </param>
        /// <param name="value">
        /// The observation value 
        /// </param>
        /// <param name="info">
        /// The current Data Retrieval state 
        /// </param>
        /// <returns>
        /// The number of observations stored. Is is always 1 
        /// </returns>
        protected static int WriteObservation(MappedXsValues row, string tag, string value, DataRetrievalInfoXS info)
        {
            // write time and obs value
            info.XSWriter.StartXSObservation(tag, value);

            // write observation attributes
            foreach (var keyValuePair in row.ObservationLevelDimensionValues)
            {
                info.XSWriter.WriteXSObservationKeyValue(keyValuePair.Key.Id, keyValuePair.Value);
            }

            // write observation attributes
            WriteAttributes(row.ObservationLevelAttributeValues, info);
            return 1;
        }
        /// <summary>
        /// Write the CrossSectional Group with the dimensions and attributes found in <paramref name="row"/>
        /// </summary>
        /// <param name="row">
        /// The map between components and their values 
        /// </param>
        /// <param name="info">
        /// The current Data Retrieval state 
        /// </param>
        protected static void WriteGroup(MappedXsValues row, DataRetrievalInfoXS info)
        {
            info.XSWriter.StartXSGroup();

            foreach (var groupLevelDimension in row.GroupLevelDimensionValues)
            {
                info.XSWriter.WriteXSGroupKeyValue(groupLevelDimension.Key.Id, groupLevelDimension.Value);
            }

            WriteAttributes(row.GroupLevelAttributeValues, info);
        }
        /// <summary>
        /// Write a primary measure observation
        /// </summary>
        /// <param name="row">
        /// The map between components and their values 
        /// </param>
        /// <param name="info">
        /// The current Data Retrieval state 
        /// </param>
        /// <param name="limit">
        /// The limit. 
        /// </param>
        /// <returns>
        /// The number of observations stored. 
        /// </returns>
        private static int WriteObservation(MappedXsValues row, DataRetrievalInfoXS info, int limit)
        {
            int maxMeasures = Math.Min(info.CrossSectionalMeasureMappings.Count, limit);

            int count = 0;
            for (var i = 0; i < maxMeasures; i++)
            {
                var crossSectionalMeasureMapping = info.CrossSectionalMeasureMappings[i];
                var xsComponent = crossSectionalMeasureMapping.Components[0];
                count += WriteObservation(row, xsComponent.Id, row.GetXSMeasureValue(xsComponent), info);
            }

            return count;
        }
        /// <summary>
        /// Write a primary measure observation
        /// </summary>
        /// <param name="row">
        /// The map between components and their values 
        /// </param>
        /// <param name="info">
        /// The current Data Retrieval state 
        /// </param>
        /// <returns>
        /// The number of observations stored. 
        /// </returns>
        protected override int WriteObservation(MappedXsValues row, DataRetrievalInfoXS info)
        {
            int count = 0;
            for (var i = 0; i < info.CrossSectionalMeasureMappings.Count; i++)
            {
                var crossSectionalMeasureMapping = info.CrossSectionalMeasureMappings[i];
                var xsComponent = crossSectionalMeasureMapping.Components[0];
                count += WriteObservation(row, xsComponent.Id, row.GetXSMeasureValue(xsComponent), info);
            }

            return count;
        }
 /// <summary>
 /// Write a primary measure observation
 /// </summary>
 /// <param name="row">
 /// The map between components and their values 
 /// </param>
 /// <param name="info">
 /// The current Data Retrieval state 
 /// </param>
 /// <returns>
 /// The number of observations stored. 
 /// </returns>
 protected override int WriteObservation(MappedXsValues row, DataRetrievalInfoXS info)
 {
     return WriteObservation(row, row.PrimaryMeasureValue.Key.Id, row.PrimaryMeasureValue.Value, info);
 }