/// <summary>
        ///   Writes the attributes to a stream.
        /// </summary>
        /// <param name="wr"><see cref="StreamWriter"/> object to write the types to.</param>
        /// <param name="attributes">Array of <see cref="AttributeSpecificationInfo"/> objects to write to the stream.</param>
        /// <param name="indentOffset">Number of indents to add before any documentation begins.</param>
        /// <param name="parentName">Name of the parent object.</param>
        /// <param name="parentType">Type of the parent object.</param>
        /// <exception cref="ArgumentException"><paramref name="parentName"/>, or <paramref name="parentType"/> is an empty string.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="wr"/>, <paramref name="parentName"/>, or <paramref name="parentType"/> is a null reference.</exception>
        /// <exception cref="InvalidOperationException">A duplicate object or name is found in the <paramref name="attributes"/>.</exception>
        /// <exception cref="IOException">An error occurred while writing to the <see cref="StreamWriter"/> object.</exception>
        public static void WriteAttributes(StreamWriter wr, AttributeSpecificationInfo[] attributes, int indentOffset, string parentName, string parentType)
        {
            if (wr == null)
            {
                throw new ArgumentNullException("wr");
            }
            if (attributes == null)
            {
                throw new ArgumentNullException("attributes");
            }
            if (parentName == null)
            {
                throw new ArgumentNullException("parentName");
            }
            if (parentName.Length == 0)
            {
                throw new ArgumentException("parentName is an empty string");
            }
            if (parentType == null)
            {
                throw new ArgumentNullException("parentType");
            }
            if (parentType.Length == 0)
            {
                throw new ArgumentException("parentType is an empty string");
            }

            if (indentOffset < 0)
            {
                indentOffset = 0;
            }

            // Write nothing if array is empty.
            if (attributes.Length == 0)
            {
                return;
            }
            // TODO: should validate that there aren't duplicate declaration/signal combination. - RD

            Dictionary <AttributeDeclarationInfo, List <AttributeSpecificationInfo> > lookup = new Dictionary <AttributeDeclarationInfo, List <AttributeSpecificationInfo> >();

            foreach (AttributeSpecificationInfo info in attributes)
            {
                if (!lookup.ContainsKey(info.Declaration))
                {
                    lookup.Add(info.Declaration, new List <AttributeSpecificationInfo>());
                }
                lookup[info.Declaration].Add(info);
            }

            // Validate that we don't have duplicate names in the declarations.
            List <AttributeDeclarationInfo> list = new List <AttributeDeclarationInfo>(lookup.Keys);

            BaseTypeInfo.ValidateNoDuplicates(list.ToArray(), parentName, parentType);
            list.Sort();

            DocumentationHelper.WriteRegionStart(wr, "Attributes", indentOffset);
            for (int i = 0; i < list.Count; i++)
            {
                list[i].Write(wr, indentOffset);

                DocumentationHelper.WriteLine(wr);
                for (int j = 0; j < lookup[list[i]].Count; j++)
                {
                    lookup[list[i]][j].Write(wr, indentOffset);
                    if (j != lookup[list[i]].Count - 1)
                    {
                        DocumentationHelper.WriteLine(wr);                         // Leave a line between specifications.
                    }
                }

                if (i != list.Count - 1)
                {
                    DocumentationHelper.WriteLine(wr);                     // Leave a line between declarations.
                }
            }
            DocumentationHelper.WriteRegionEnd(wr, "Attributes", indentOffset);
        }