/// <summary>
        /// Imprime um item tabular para o elemento da tabela.
        /// </summary>
        /// <typeparam name="R">O tipo dos objectos que constituem as linhas do item tabular.</typeparam>
        /// <typeparam name="L">O tipo dos objectos que constituem as células do item tabular.</typeparam>
        /// <param name="cellElementName">O nome do elemento da célula.</param>
        /// <param name="element">O elemento da tabela que irá conter as linhas.</param>
        /// <param name="columnsNumber">O número de colunas esperado.</param>
        /// <param name="tabularItem">O item tabular.</param>
        /// <param name="mergingRegions">O conjunto de regiões de células fundidas.</param>
        /// <param name="attributessSetter">A função responsável pela adição de atributos.</param>
        /// <param name="elementsSetter">A função responsável pelo processamento de elementos.</param>
        private void PrintTabularItem <R, L>(
            string cellElementName,
            XmlElement element,
            int columnsNumber,
            IGeneralTabularItem <R> tabularItem,
            NonIntersectingMergingRegionsSet <int, MergingRegion <int> > mergingRegions,
            Action <int, AttributeSetter> attributessSetter,
            Action <int, int, object, ElementSetter> elementsSetter)
            where R : IGeneralTabularRow <L>
            where L : IGeneralTabularCell
        {
            var attributeSetter            = new AttributeSetter();
            var validatedCellElementSetter = new ValidatedCellElementSetter();
            var linesNumber = tabularItem.LastRowNumber + 1;
            var linePointer = 0;

            foreach (var tabularLine in tabularItem)
            {
                var currentLine = tabularLine.RowNumber;
                for (; linePointer < currentLine; ++linePointer)
                {
                    var rowElement = element.OwnerDocument.CreateElement("tr");
                    attributeSetter.CurrentElement = rowElement;
                    attributessSetter.Invoke(
                        linePointer,
                        attributeSetter);
                    this.AppendEmptyCells(
                        cellElementName,
                        rowElement,
                        linePointer,
                        columnsNumber,
                        linesNumber,
                        columnsNumber,
                        validatedCellElementSetter,
                        mergingRegions,
                        elementsSetter);
                    element.AppendChild(rowElement);
                }

                var lineElement = element.OwnerDocument.CreateElement("tr");
                attributeSetter.CurrentElement = lineElement;
                attributessSetter.Invoke(
                    linePointer,
                    attributeSetter);
                var currentCellNumber = 0;
                foreach (var tabularCell in tabularLine)
                {
                    if (currentCellNumber <= tabularCell.ColumnNumber)
                    {
                        var emptyCellsCount = tabularCell.ColumnNumber - currentCellNumber;
                        this.AppendEmptyCells(
                            cellElementName,
                            lineElement,
                            linePointer,
                            emptyCellsCount,
                            linesNumber,
                            columnsNumber,
                            validatedCellElementSetter,
                            mergingRegions,
                            elementsSetter);

                        var mergingRegion = mergingRegions.GetMergingRegionForCell(
                            tabularCell.ColumnNumber,
                            currentLine);
                        if (mergingRegion == null)
                        {
                            var tableCellElement = lineElement.OwnerDocument.CreateElement(cellElementName);
                            validatedCellElementSetter.CurrentElement = tableCellElement;
                            lineElement.AppendChild(tableCellElement);

                            elementsSetter.Invoke(
                                currentLine,
                                tabularCell.ColumnNumber,
                                tabularCell.GetCellValue <object>(),
                                validatedCellElementSetter);
                            lineElement.AppendChild(tableCellElement);
                            currentCellNumber = tabularCell.ColumnNumber + 1;
                        }
                        else if (mergingRegion.TopLeftX == tabularCell.ColumnNumber)
                        {
                            var colDifference = mergingRegion.BottomRightX - mergingRegion.TopLeftX;
                            if (mergingRegion.TopLeftY == currentLine)
                            {
                                var tableCellElement = lineElement.OwnerDocument.CreateElement(cellElementName);
                                validatedCellElementSetter.CurrentElement = tableCellElement;
                                lineElement.AppendChild(tableCellElement);
                                elementsSetter.Invoke(
                                    currentLine,
                                    tabularCell.ColumnNumber,
                                    tabularCell.GetCellValue <object>(),
                                    validatedCellElementSetter);

                                if (colDifference > 0)
                                {
                                    var colSpan = Math.Min(
                                        colDifference + 1,
                                        columnsNumber - tabularCell.ColumnNumber);
                                    if (colSpan > 1)
                                    {
                                        tableCellElement.SetAttribute("colspan", colSpan.ToString());
                                    }
                                }

                                var rowDifference = mergingRegion.BottomRightY - mergingRegion.TopLeftY;
                                if (rowDifference > 0)
                                {
                                    var rowSpan = Math.Min(
                                        rowDifference + 1,
                                        linesNumber - currentLine);
                                    if (rowSpan > 1)
                                    {
                                        tableCellElement.SetAttribute("rowspan", rowSpan.ToString());
                                    }
                                }
                            }

                            currentCellNumber = tabularCell.ColumnNumber + colDifference + 1;
                        }
                    }
                }

                var finalEmptyCellsCount = columnsNumber - currentCellNumber;
                this.AppendEmptyCells(
                    cellElementName,
                    lineElement,
                    linePointer,
                    finalEmptyCellsCount,
                    linesNumber,
                    columnsNumber,
                    validatedCellElementSetter,
                    mergingRegions,
                    elementsSetter);

                element.AppendChild(lineElement);
                ++linePointer;
            }

            // Inserção das restantes linhas vazias
            for (; linePointer < linesNumber; ++linePointer)
            {
                var rowElement = element.OwnerDocument.CreateElement("th");
                attributeSetter.CurrentElement = rowElement;
                this.headerRowAttributesSetter.Invoke(
                    linePointer,
                    attributeSetter);
                this.AppendEmptyCells(
                    cellElementName,
                    rowElement,
                    linePointer,
                    columnsNumber,
                    linesNumber,
                    columnsNumber,
                    validatedCellElementSetter,
                    mergingRegions,
                    elementsSetter);
            }
        }
        /// <summary>
        /// Adenda células do vazias a uma linha do cabeçalho.
        /// </summary>
        /// <param name="cellElementName">O nome da célula.</param>
        /// <param name="lineElement">A linha à qual serão adicionadas células vazias.</param>
        /// <param name="currentLine">O número da linha de inserção.</param>
        /// <param name="count">O número de elementos a inserir.</param>
        /// <param name="linesNumber">O número de linhas na tabela.</param>
        /// <param name="columnsNumber">O número de colunas.</param>
        /// <param name="elementSetter">O objecto responsável pela alteração do elemento.</param>
        /// <param name="mergingRegions">As regiões de fusão de células.</param>
        /// <param name="elementsSetter">A função responsável pelo processamento dos elementos.</param>
        private void AppendEmptyCells(
            string cellElementName,
            XmlElement lineElement,
            int currentLine,
            int count,
            int linesNumber,
            int columnsNumber,
            ValidatedCellElementSetter elementSetter,
            NonIntersectingMergingRegionsSet <int, MergingRegion <int> > mergingRegions,
            Action <int, int, object, ElementSetter> elementsSetter)
        {
            for (int i = 0; i < count; ++i)
            {
                var mergingRegion = mergingRegions.GetMergingRegionForCell(
                    i,
                    currentLine);
                if (mergingRegion == null)
                {
                    var tableCellElement = lineElement.OwnerDocument.CreateElement(cellElementName);
                    elementSetter.CurrentElement = tableCellElement;
                    lineElement.AppendChild(tableCellElement);

                    elementsSetter.Invoke(
                        currentLine,
                        i,
                        null,
                        elementSetter);
                    lineElement.AppendChild(tableCellElement);
                }
                else if (mergingRegion.TopLeftX == i)
                {
                    var colDifference = mergingRegion.BottomRightX - mergingRegion.TopLeftY;
                    if (mergingRegion.TopLeftY == currentLine)
                    {
                        var tableCellElement = lineElement.OwnerDocument.CreateElement(cellElementName);
                        elementSetter.CurrentElement = tableCellElement;
                        lineElement.AppendChild(tableCellElement);
                        elementsSetter.Invoke(
                            currentLine,
                            i,
                            null,
                            elementSetter);

                        if (colDifference > 0)
                        {
                            var colSpan = Math.Min(
                                colDifference + 1,
                                columnsNumber - i);
                            if (colSpan > 1)
                            {
                                tableCellElement.SetAttribute("colspan", colSpan.ToString());
                            }
                        }

                        var rowDifference = mergingRegion.BottomRightY - mergingRegion.TopLeftY;
                        if (rowDifference > 0)
                        {
                            var rowSpan = Math.Min(
                                rowDifference + 1,
                                linesNumber - currentLine);
                            if (rowSpan > 1)
                            {
                                tableCellElement.SetAttribute("rowspan", rowSpan.ToString());
                            }
                        }
                    }

                    i += colDifference;
                }
            }
        }