/// <summary>
        /// Get all the pins exposed by the implementor.
        /// </summary>
        /// <remarks>If operations have tags, the operations with tags are
        /// grouped by them, and a pin is returned for each group, with its name
        /// equal to <c>fr.msp</c> + the grouped operations tag, and its value
        /// equal to the count of such operations. These pins are sorted
        /// by their name.</remarks>
        /// <returns>pins</returns>
        public IEnumerable <DataPin> GetDataPins()
        {
            if (Operations?.Count == 0)
            {
                return(Enumerable.Empty <DataPin>());
            }

            var groups = from s in Operations
                         let o = MspOperation.Parse(s)
                                 where o?.Tag != null
                                 group o by o.Tag
                                 into g
                                 select g;

            return(from g in groups
                   orderby g.Key
                   select new DataPin
            {
                Name = PartBase.FR_PREFIX + $"msp.{g.Key}",
                Value = g.Count().ToString(CultureInfo.InvariantCulture)
            });
        }
        /// <summary>
        /// Get all the pins exposed by the implementor.
        /// </summary>
        /// <param name="item">The optional item. The item with its parts
        /// can optionally be passed to this method for those parts requiring
        /// to access further data.</param>
        /// <remarks>If operations have tags, the operations with tags are
        /// grouped by them, and a pin is returned for each group, with its name
        /// equal to <c>fr.msp</c> + the grouped operations tag, and its value
        /// equal to the count of such operations. These pins are sorted
        /// by their name.
        /// <para>Also, if <paramref name="item"/> is received and it has
        /// a base text part and an orthography layer part, two additional pins
        /// are returned: <c>fr.orthography-txt</c> with the original orthography
        /// got from the base text, and <c>fr.orthography.std</c> with the
        /// <see cref="Standard"/> orthography from this fragment.</para>
        /// </remarks>
        /// <returns>pins</returns>
        public IEnumerable <DataPin> GetDataPins(IItem item = null)
        {
            List <DataPin> pins = new List <DataPin>();

            if (Operations?.Count > 0)
            {
                var groups = from s in Operations
                             let o = MspOperation.Parse(s)
                                     where o?.Tag != null
                                     group o by o.Tag
                                     into g
                                     select g;

                pins.AddRange(
                    from g in groups
                    orderby g.Key
                    select new DataPin
                {
                    Name  = PartBase.FR_PREFIX + $"msp.{g.Key}",
                    Value = g.Count().ToString(CultureInfo.InvariantCulture)
                });
            }

            if (item != null)
            {
                // get the base text part
                IPart textPart = item.Parts
                                 .Find(p => p.RoleId == PartBase.BASE_TEXT_ROLE_ID);
                if (textPart == null)
                {
                    return(pins);
                }

                // get the orthography layer
                TagAttribute attr = GetType().GetTypeInfo()
                                    .GetCustomAttribute <TagAttribute>();
                Regex roleIdRegex = new Regex("^" + attr.Tag + "(?::.+)?$");

                IHasFragments <OrthographyLayerFragment> layerPart =
                    item.Parts.Find(p => p.RoleId != null &&
                                    roleIdRegex.IsMatch(p.RoleId))
                    as IHasFragments <OrthographyLayerFragment>;
                if (layerPart == null)
                {
                    return(pins);
                }

                string baseText = layerPart.GetTextAt(textPart, Location);
                if (baseText != null)
                {
                    pins.Add(new DataPin
                    {
                        Name  = PartBase.FR_PREFIX + "orthography-txt",
                        Value = baseText
                    });
                    pins.Add(new DataPin
                    {
                        Name  = PartBase.FR_PREFIX + "orthography-std",
                        Value = Standard
                    });
                }
            }

            return(pins);
        }