942 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			942 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Go
		
	
	
	
// Copyright 2014 Oleku Konko All rights reserved.
 | 
						|
// Use of this source code is governed by a MIT
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
// This module is a Table Writer  API for the Go Programming Language.
 | 
						|
// The protocols were written in pure Go and works on windows and unix systems
 | 
						|
 | 
						|
// Create & Generate text based table
 | 
						|
package tablewriter
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"regexp"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	MAX_ROW_WIDTH = 30
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	CENTER  = "+"
 | 
						|
	ROW     = "-"
 | 
						|
	COLUMN  = "|"
 | 
						|
	SPACE   = " "
 | 
						|
	NEWLINE = "\n"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	ALIGN_DEFAULT = iota
 | 
						|
	ALIGN_CENTER
 | 
						|
	ALIGN_RIGHT
 | 
						|
	ALIGN_LEFT
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	decimal = regexp.MustCompile(`^-?(?:\d{1,3}(?:,\d{3})*|\d+)(?:\.\d+)?$`)
 | 
						|
	percent = regexp.MustCompile(`^-?\d+\.?\d*$%$`)
 | 
						|
)
 | 
						|
 | 
						|
type Border struct {
 | 
						|
	Left   bool
 | 
						|
	Right  bool
 | 
						|
	Top    bool
 | 
						|
	Bottom bool
 | 
						|
}
 | 
						|
 | 
						|
type Table struct {
 | 
						|
	out            io.Writer
 | 
						|
	rows           [][]string
 | 
						|
	lines          [][][]string
 | 
						|
	cs             map[int]int
 | 
						|
	rs             map[int]int
 | 
						|
	headers        [][]string
 | 
						|
	footers        [][]string
 | 
						|
	caption        bool
 | 
						|
	captionText    string
 | 
						|
	autoFmt        bool
 | 
						|
	autoWrap       bool
 | 
						|
	reflowText     bool
 | 
						|
	mW             int
 | 
						|
	pCenter        string
 | 
						|
	pRow           string
 | 
						|
	pColumn        string
 | 
						|
	tColumn        int
 | 
						|
	tRow           int
 | 
						|
	hAlign         int
 | 
						|
	fAlign         int
 | 
						|
	align          int
 | 
						|
	newLine        string
 | 
						|
	rowLine        bool
 | 
						|
	autoMergeCells bool
 | 
						|
	noWhiteSpace   bool
 | 
						|
	tablePadding   string
 | 
						|
	hdrLine        bool
 | 
						|
	borders        Border
 | 
						|
	colSize        int
 | 
						|
	headerParams   []string
 | 
						|
	columnsParams  []string
 | 
						|
	footerParams   []string
 | 
						|
	columnsAlign   []int
 | 
						|
}
 | 
						|
 | 
						|
// Start New Table
 | 
						|
// Take io.Writer Directly
 | 
						|
func NewWriter(writer io.Writer) *Table {
 | 
						|
	t := &Table{
 | 
						|
		out:           writer,
 | 
						|
		rows:          [][]string{},
 | 
						|
		lines:         [][][]string{},
 | 
						|
		cs:            make(map[int]int),
 | 
						|
		rs:            make(map[int]int),
 | 
						|
		headers:       [][]string{},
 | 
						|
		footers:       [][]string{},
 | 
						|
		caption:       false,
 | 
						|
		captionText:   "Table caption.",
 | 
						|
		autoFmt:       true,
 | 
						|
		autoWrap:      true,
 | 
						|
		reflowText:    true,
 | 
						|
		mW:            MAX_ROW_WIDTH,
 | 
						|
		pCenter:       CENTER,
 | 
						|
		pRow:          ROW,
 | 
						|
		pColumn:       COLUMN,
 | 
						|
		tColumn:       -1,
 | 
						|
		tRow:          -1,
 | 
						|
		hAlign:        ALIGN_DEFAULT,
 | 
						|
		fAlign:        ALIGN_DEFAULT,
 | 
						|
		align:         ALIGN_DEFAULT,
 | 
						|
		newLine:       NEWLINE,
 | 
						|
		rowLine:       false,
 | 
						|
		hdrLine:       true,
 | 
						|
		borders:       Border{Left: true, Right: true, Bottom: true, Top: true},
 | 
						|
		colSize:       -1,
 | 
						|
		headerParams:  []string{},
 | 
						|
		columnsParams: []string{},
 | 
						|
		footerParams:  []string{},
 | 
						|
		columnsAlign:  []int{}}
 | 
						|
	return t
 | 
						|
}
 | 
						|
 | 
						|
// Render table output
 | 
						|
func (t *Table) Render() {
 | 
						|
	if t.borders.Top {
 | 
						|
		t.printLine(true)
 | 
						|
	}
 | 
						|
	t.printHeading()
 | 
						|
	if t.autoMergeCells {
 | 
						|
		t.printRowsMergeCells()
 | 
						|
	} else {
 | 
						|
		t.printRows()
 | 
						|
	}
 | 
						|
	if !t.rowLine && t.borders.Bottom {
 | 
						|
		t.printLine(true)
 | 
						|
	}
 | 
						|
	t.printFooter()
 | 
						|
 | 
						|
	if t.caption {
 | 
						|
		t.printCaption()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	headerRowIdx = -1
 | 
						|
	footerRowIdx = -2
 | 
						|
)
 | 
						|
 | 
						|
// Set table header
 | 
						|
func (t *Table) SetHeader(keys []string) {
 | 
						|
	t.colSize = len(keys)
 | 
						|
	for i, v := range keys {
 | 
						|
		lines := t.parseDimension(v, i, headerRowIdx)
 | 
						|
		t.headers = append(t.headers, lines)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Set table Footer
 | 
						|
func (t *Table) SetFooter(keys []string) {
 | 
						|
	//t.colSize = len(keys)
 | 
						|
	for i, v := range keys {
 | 
						|
		lines := t.parseDimension(v, i, footerRowIdx)
 | 
						|
		t.footers = append(t.footers, lines)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Set table Caption
 | 
						|
func (t *Table) SetCaption(caption bool, captionText ...string) {
 | 
						|
	t.caption = caption
 | 
						|
	if len(captionText) == 1 {
 | 
						|
		t.captionText = captionText[0]
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Turn header autoformatting on/off. Default is on (true).
 | 
						|
func (t *Table) SetAutoFormatHeaders(auto bool) {
 | 
						|
	t.autoFmt = auto
 | 
						|
}
 | 
						|
 | 
						|
// Turn automatic multiline text adjustment on/off. Default is on (true).
 | 
						|
func (t *Table) SetAutoWrapText(auto bool) {
 | 
						|
	t.autoWrap = auto
 | 
						|
}
 | 
						|
 | 
						|
// Turn automatic reflowing of multiline text when rewrapping. Default is on (true).
 | 
						|
func (t *Table) SetReflowDuringAutoWrap(auto bool) {
 | 
						|
	t.reflowText = auto
 | 
						|
}
 | 
						|
 | 
						|
// Set the Default column width
 | 
						|
func (t *Table) SetColWidth(width int) {
 | 
						|
	t.mW = width
 | 
						|
}
 | 
						|
 | 
						|
// Set the minimal width for a column
 | 
						|
func (t *Table) SetColMinWidth(column int, width int) {
 | 
						|
	t.cs[column] = width
 | 
						|
}
 | 
						|
 | 
						|
// Set the Column Separator
 | 
						|
func (t *Table) SetColumnSeparator(sep string) {
 | 
						|
	t.pColumn = sep
 | 
						|
}
 | 
						|
 | 
						|
// Set the Row Separator
 | 
						|
func (t *Table) SetRowSeparator(sep string) {
 | 
						|
	t.pRow = sep
 | 
						|
}
 | 
						|
 | 
						|
// Set the center Separator
 | 
						|
func (t *Table) SetCenterSeparator(sep string) {
 | 
						|
	t.pCenter = sep
 | 
						|
}
 | 
						|
 | 
						|
// Set Header Alignment
 | 
						|
func (t *Table) SetHeaderAlignment(hAlign int) {
 | 
						|
	t.hAlign = hAlign
 | 
						|
}
 | 
						|
 | 
						|
// Set Footer Alignment
 | 
						|
func (t *Table) SetFooterAlignment(fAlign int) {
 | 
						|
	t.fAlign = fAlign
 | 
						|
}
 | 
						|
 | 
						|
// Set Table Alignment
 | 
						|
func (t *Table) SetAlignment(align int) {
 | 
						|
	t.align = align
 | 
						|
}
 | 
						|
 | 
						|
// Set No White Space
 | 
						|
func (t *Table) SetNoWhiteSpace(allow bool) {
 | 
						|
	t.noWhiteSpace = allow
 | 
						|
}
 | 
						|
 | 
						|
// Set Table Padding
 | 
						|
func (t *Table) SetTablePadding(padding string) {
 | 
						|
	t.tablePadding = padding
 | 
						|
}
 | 
						|
 | 
						|
func (t *Table) SetColumnAlignment(keys []int) {
 | 
						|
	for _, v := range keys {
 | 
						|
		switch v {
 | 
						|
		case ALIGN_CENTER:
 | 
						|
			break
 | 
						|
		case ALIGN_LEFT:
 | 
						|
			break
 | 
						|
		case ALIGN_RIGHT:
 | 
						|
			break
 | 
						|
		default:
 | 
						|
			v = ALIGN_DEFAULT
 | 
						|
		}
 | 
						|
		t.columnsAlign = append(t.columnsAlign, v)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Set New Line
 | 
						|
func (t *Table) SetNewLine(nl string) {
 | 
						|
	t.newLine = nl
 | 
						|
}
 | 
						|
 | 
						|
// Set Header Line
 | 
						|
// This would enable / disable a line after the header
 | 
						|
func (t *Table) SetHeaderLine(line bool) {
 | 
						|
	t.hdrLine = line
 | 
						|
}
 | 
						|
 | 
						|
// Set Row Line
 | 
						|
// This would enable / disable a line on each row of the table
 | 
						|
func (t *Table) SetRowLine(line bool) {
 | 
						|
	t.rowLine = line
 | 
						|
}
 | 
						|
 | 
						|
// Set Auto Merge Cells
 | 
						|
// This would enable / disable the merge of cells with identical values
 | 
						|
func (t *Table) SetAutoMergeCells(auto bool) {
 | 
						|
	t.autoMergeCells = auto
 | 
						|
}
 | 
						|
 | 
						|
// Set Table Border
 | 
						|
// This would enable / disable line around the table
 | 
						|
func (t *Table) SetBorder(border bool) {
 | 
						|
	t.SetBorders(Border{border, border, border, border})
 | 
						|
}
 | 
						|
 | 
						|
func (t *Table) SetBorders(border Border) {
 | 
						|
	t.borders = border
 | 
						|
}
 | 
						|
 | 
						|
// Append row to table
 | 
						|
func (t *Table) Append(row []string) {
 | 
						|
	rowSize := len(t.headers)
 | 
						|
	if rowSize > t.colSize {
 | 
						|
		t.colSize = rowSize
 | 
						|
	}
 | 
						|
 | 
						|
	n := len(t.lines)
 | 
						|
	line := [][]string{}
 | 
						|
	for i, v := range row {
 | 
						|
 | 
						|
		// Detect string  width
 | 
						|
		// Detect String height
 | 
						|
		// Break strings into words
 | 
						|
		out := t.parseDimension(v, i, n)
 | 
						|
 | 
						|
		// Append broken words
 | 
						|
		line = append(line, out)
 | 
						|
	}
 | 
						|
	t.lines = append(t.lines, line)
 | 
						|
}
 | 
						|
 | 
						|
// Append row to table with color attributes
 | 
						|
func (t *Table) Rich(row []string, colors []Colors) {
 | 
						|
	rowSize := len(t.headers)
 | 
						|
	if rowSize > t.colSize {
 | 
						|
		t.colSize = rowSize
 | 
						|
	}
 | 
						|
 | 
						|
	n := len(t.lines)
 | 
						|
	line := [][]string{}
 | 
						|
	for i, v := range row {
 | 
						|
 | 
						|
		// Detect string  width
 | 
						|
		// Detect String height
 | 
						|
		// Break strings into words
 | 
						|
		out := t.parseDimension(v, i, n)
 | 
						|
 | 
						|
		if len(colors) > i {
 | 
						|
			color := colors[i]
 | 
						|
			out[0] = format(out[0], color)
 | 
						|
		}
 | 
						|
 | 
						|
		// Append broken words
 | 
						|
		line = append(line, out)
 | 
						|
	}
 | 
						|
	t.lines = append(t.lines, line)
 | 
						|
}
 | 
						|
 | 
						|
// Allow Support for Bulk Append
 | 
						|
// Eliminates repeated for loops
 | 
						|
func (t *Table) AppendBulk(rows [][]string) {
 | 
						|
	for _, row := range rows {
 | 
						|
		t.Append(row)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// NumLines to get the number of lines
 | 
						|
func (t *Table) NumLines() int {
 | 
						|
	return len(t.lines)
 | 
						|
}
 | 
						|
 | 
						|
// Clear rows
 | 
						|
func (t *Table) ClearRows() {
 | 
						|
	t.lines = [][][]string{}
 | 
						|
}
 | 
						|
 | 
						|
// Clear footer
 | 
						|
func (t *Table) ClearFooter() {
 | 
						|
	t.footers = [][]string{}
 | 
						|
}
 | 
						|
 | 
						|
// Center based on position and border.
 | 
						|
func (t *Table) center(i int) string {
 | 
						|
	if i == -1 && !t.borders.Left {
 | 
						|
		return t.pRow
 | 
						|
	}
 | 
						|
 | 
						|
	if i == len(t.cs)-1 && !t.borders.Right {
 | 
						|
		return t.pRow
 | 
						|
	}
 | 
						|
 | 
						|
	return t.pCenter
 | 
						|
}
 | 
						|
 | 
						|
// Print line based on row width
 | 
						|
func (t *Table) printLine(nl bool) {
 | 
						|
	fmt.Fprint(t.out, t.center(-1))
 | 
						|
	for i := 0; i < len(t.cs); i++ {
 | 
						|
		v := t.cs[i]
 | 
						|
		fmt.Fprintf(t.out, "%s%s%s%s",
 | 
						|
			t.pRow,
 | 
						|
			strings.Repeat(string(t.pRow), v),
 | 
						|
			t.pRow,
 | 
						|
			t.center(i))
 | 
						|
	}
 | 
						|
	if nl {
 | 
						|
		fmt.Fprint(t.out, t.newLine)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Print line based on row width with our without cell separator
 | 
						|
func (t *Table) printLineOptionalCellSeparators(nl bool, displayCellSeparator []bool) {
 | 
						|
	fmt.Fprint(t.out, t.pCenter)
 | 
						|
	for i := 0; i < len(t.cs); i++ {
 | 
						|
		v := t.cs[i]
 | 
						|
		if i > len(displayCellSeparator) || displayCellSeparator[i] {
 | 
						|
			// Display the cell separator
 | 
						|
			fmt.Fprintf(t.out, "%s%s%s%s",
 | 
						|
				t.pRow,
 | 
						|
				strings.Repeat(string(t.pRow), v),
 | 
						|
				t.pRow,
 | 
						|
				t.pCenter)
 | 
						|
		} else {
 | 
						|
			// Don't display the cell separator for this cell
 | 
						|
			fmt.Fprintf(t.out, "%s%s",
 | 
						|
				strings.Repeat(" ", v+2),
 | 
						|
				t.pCenter)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if nl {
 | 
						|
		fmt.Fprint(t.out, t.newLine)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Return the PadRight function if align is left, PadLeft if align is right,
 | 
						|
// and Pad by default
 | 
						|
func pad(align int) func(string, string, int) string {
 | 
						|
	padFunc := Pad
 | 
						|
	switch align {
 | 
						|
	case ALIGN_LEFT:
 | 
						|
		padFunc = PadRight
 | 
						|
	case ALIGN_RIGHT:
 | 
						|
		padFunc = PadLeft
 | 
						|
	}
 | 
						|
	return padFunc
 | 
						|
}
 | 
						|
 | 
						|
// Print heading information
 | 
						|
func (t *Table) printHeading() {
 | 
						|
	// Check if headers is available
 | 
						|
	if len(t.headers) < 1 {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// Identify last column
 | 
						|
	end := len(t.cs) - 1
 | 
						|
 | 
						|
	// Get pad function
 | 
						|
	padFunc := pad(t.hAlign)
 | 
						|
 | 
						|
	// Checking for ANSI escape sequences for header
 | 
						|
	is_esc_seq := false
 | 
						|
	if len(t.headerParams) > 0 {
 | 
						|
		is_esc_seq = true
 | 
						|
	}
 | 
						|
 | 
						|
	// Maximum height.
 | 
						|
	max := t.rs[headerRowIdx]
 | 
						|
 | 
						|
	// Print Heading
 | 
						|
	for x := 0; x < max; x++ {
 | 
						|
		// Check if border is set
 | 
						|
		// Replace with space if not set
 | 
						|
		if !t.noWhiteSpace {
 | 
						|
			fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
 | 
						|
		}
 | 
						|
 | 
						|
		for y := 0; y <= end; y++ {
 | 
						|
			v := t.cs[y]
 | 
						|
			h := ""
 | 
						|
 | 
						|
			if y < len(t.headers) && x < len(t.headers[y]) {
 | 
						|
				h = t.headers[y][x]
 | 
						|
			}
 | 
						|
			if t.autoFmt {
 | 
						|
				h = Title(h)
 | 
						|
			}
 | 
						|
			pad := ConditionString((y == end && !t.borders.Left), SPACE, t.pColumn)
 | 
						|
			if t.noWhiteSpace {
 | 
						|
				pad = ConditionString((y == end && !t.borders.Left), SPACE, t.tablePadding)
 | 
						|
			}
 | 
						|
			if is_esc_seq {
 | 
						|
				if !t.noWhiteSpace {
 | 
						|
					fmt.Fprintf(t.out, " %s %s",
 | 
						|
						format(padFunc(h, SPACE, v),
 | 
						|
							t.headerParams[y]), pad)
 | 
						|
				} else {
 | 
						|
					fmt.Fprintf(t.out, "%s %s",
 | 
						|
						format(padFunc(h, SPACE, v),
 | 
						|
							t.headerParams[y]), pad)
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				if !t.noWhiteSpace {
 | 
						|
					fmt.Fprintf(t.out, " %s %s",
 | 
						|
						padFunc(h, SPACE, v),
 | 
						|
						pad)
 | 
						|
				} else {
 | 
						|
					// the spaces between breaks the kube formatting
 | 
						|
					fmt.Fprintf(t.out, "%s%s",
 | 
						|
						padFunc(h, SPACE, v),
 | 
						|
						pad)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		// Next line
 | 
						|
		fmt.Fprint(t.out, t.newLine)
 | 
						|
	}
 | 
						|
	if t.hdrLine {
 | 
						|
		t.printLine(true)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Print heading information
 | 
						|
func (t *Table) printFooter() {
 | 
						|
	// Check if headers is available
 | 
						|
	if len(t.footers) < 1 {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// Only print line if border is not set
 | 
						|
	if !t.borders.Bottom {
 | 
						|
		t.printLine(true)
 | 
						|
	}
 | 
						|
 | 
						|
	// Identify last column
 | 
						|
	end := len(t.cs) - 1
 | 
						|
 | 
						|
	// Get pad function
 | 
						|
	padFunc := pad(t.fAlign)
 | 
						|
 | 
						|
	// Checking for ANSI escape sequences for header
 | 
						|
	is_esc_seq := false
 | 
						|
	if len(t.footerParams) > 0 {
 | 
						|
		is_esc_seq = true
 | 
						|
	}
 | 
						|
 | 
						|
	// Maximum height.
 | 
						|
	max := t.rs[footerRowIdx]
 | 
						|
 | 
						|
	// Print Footer
 | 
						|
	erasePad := make([]bool, len(t.footers))
 | 
						|
	for x := 0; x < max; x++ {
 | 
						|
		// Check if border is set
 | 
						|
		// Replace with space if not set
 | 
						|
		fmt.Fprint(t.out, ConditionString(t.borders.Bottom, t.pColumn, SPACE))
 | 
						|
 | 
						|
		for y := 0; y <= end; y++ {
 | 
						|
			v := t.cs[y]
 | 
						|
			f := ""
 | 
						|
			if y < len(t.footers) && x < len(t.footers[y]) {
 | 
						|
				f = t.footers[y][x]
 | 
						|
			}
 | 
						|
			if t.autoFmt {
 | 
						|
				f = Title(f)
 | 
						|
			}
 | 
						|
			pad := ConditionString((y == end && !t.borders.Top), SPACE, t.pColumn)
 | 
						|
 | 
						|
			if erasePad[y] || (x == 0 && len(f) == 0) {
 | 
						|
				pad = SPACE
 | 
						|
				erasePad[y] = true
 | 
						|
			}
 | 
						|
 | 
						|
			if is_esc_seq {
 | 
						|
				fmt.Fprintf(t.out, " %s %s",
 | 
						|
					format(padFunc(f, SPACE, v),
 | 
						|
						t.footerParams[y]), pad)
 | 
						|
			} else {
 | 
						|
				fmt.Fprintf(t.out, " %s %s",
 | 
						|
					padFunc(f, SPACE, v),
 | 
						|
					pad)
 | 
						|
			}
 | 
						|
 | 
						|
			//fmt.Fprintf(t.out, " %s %s",
 | 
						|
			//	padFunc(f, SPACE, v),
 | 
						|
			//	pad)
 | 
						|
		}
 | 
						|
		// Next line
 | 
						|
		fmt.Fprint(t.out, t.newLine)
 | 
						|
		//t.printLine(true)
 | 
						|
	}
 | 
						|
 | 
						|
	hasPrinted := false
 | 
						|
 | 
						|
	for i := 0; i <= end; i++ {
 | 
						|
		v := t.cs[i]
 | 
						|
		pad := t.pRow
 | 
						|
		center := t.pCenter
 | 
						|
		length := len(t.footers[i][0])
 | 
						|
 | 
						|
		if length > 0 {
 | 
						|
			hasPrinted = true
 | 
						|
		}
 | 
						|
 | 
						|
		// Set center to be space if length is 0
 | 
						|
		if length == 0 && !t.borders.Right {
 | 
						|
			center = SPACE
 | 
						|
		}
 | 
						|
 | 
						|
		// Print first junction
 | 
						|
		if i == 0 {
 | 
						|
			if length > 0 && !t.borders.Left {
 | 
						|
				center = t.pRow
 | 
						|
			}
 | 
						|
			fmt.Fprint(t.out, center)
 | 
						|
		}
 | 
						|
 | 
						|
		// Pad With space of length is 0
 | 
						|
		if length == 0 {
 | 
						|
			pad = SPACE
 | 
						|
		}
 | 
						|
		// Ignore left space as it has printed before
 | 
						|
		if hasPrinted || t.borders.Left {
 | 
						|
			pad = t.pRow
 | 
						|
			center = t.pCenter
 | 
						|
		}
 | 
						|
 | 
						|
		// Change Center end position
 | 
						|
		if center != SPACE {
 | 
						|
			if i == end && !t.borders.Right {
 | 
						|
				center = t.pRow
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// Change Center start position
 | 
						|
		if center == SPACE {
 | 
						|
			if i < end && len(t.footers[i+1][0]) != 0 {
 | 
						|
				if !t.borders.Left {
 | 
						|
					center = t.pRow
 | 
						|
				} else {
 | 
						|
					center = t.pCenter
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// Print the footer
 | 
						|
		fmt.Fprintf(t.out, "%s%s%s%s",
 | 
						|
			pad,
 | 
						|
			strings.Repeat(string(pad), v),
 | 
						|
			pad,
 | 
						|
			center)
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	fmt.Fprint(t.out, t.newLine)
 | 
						|
}
 | 
						|
 | 
						|
// Print caption text
 | 
						|
func (t Table) printCaption() {
 | 
						|
	width := t.getTableWidth()
 | 
						|
	paragraph, _ := WrapString(t.captionText, width)
 | 
						|
	for linecount := 0; linecount < len(paragraph); linecount++ {
 | 
						|
		fmt.Fprintln(t.out, paragraph[linecount])
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Calculate the total number of characters in a row
 | 
						|
func (t Table) getTableWidth() int {
 | 
						|
	var chars int
 | 
						|
	for _, v := range t.cs {
 | 
						|
		chars += v
 | 
						|
	}
 | 
						|
 | 
						|
	// Add chars, spaces, seperators to calculate the total width of the table.
 | 
						|
	// ncols := t.colSize
 | 
						|
	// spaces := ncols * 2
 | 
						|
	// seps := ncols + 1
 | 
						|
 | 
						|
	return (chars + (3 * t.colSize) + 2)
 | 
						|
}
 | 
						|
 | 
						|
func (t Table) printRows() {
 | 
						|
	for i, lines := range t.lines {
 | 
						|
		t.printRow(lines, i)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (t *Table) fillAlignment(num int) {
 | 
						|
	if len(t.columnsAlign) < num {
 | 
						|
		t.columnsAlign = make([]int, num)
 | 
						|
		for i := range t.columnsAlign {
 | 
						|
			t.columnsAlign[i] = t.align
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Print Row Information
 | 
						|
// Adjust column alignment based on type
 | 
						|
 | 
						|
func (t *Table) printRow(columns [][]string, rowIdx int) {
 | 
						|
	// Get Maximum Height
 | 
						|
	max := t.rs[rowIdx]
 | 
						|
	total := len(columns)
 | 
						|
 | 
						|
	// TODO Fix uneven col size
 | 
						|
	// if total < t.colSize {
 | 
						|
	//	for n := t.colSize - total; n < t.colSize ; n++ {
 | 
						|
	//		columns = append(columns, []string{SPACE})
 | 
						|
	//		t.cs[n] = t.mW
 | 
						|
	//	}
 | 
						|
	//}
 | 
						|
 | 
						|
	// Pad Each Height
 | 
						|
	pads := []int{}
 | 
						|
 | 
						|
	// Checking for ANSI escape sequences for columns
 | 
						|
	is_esc_seq := false
 | 
						|
	if len(t.columnsParams) > 0 {
 | 
						|
		is_esc_seq = true
 | 
						|
	}
 | 
						|
	t.fillAlignment(total)
 | 
						|
 | 
						|
	for i, line := range columns {
 | 
						|
		length := len(line)
 | 
						|
		pad := max - length
 | 
						|
		pads = append(pads, pad)
 | 
						|
		for n := 0; n < pad; n++ {
 | 
						|
			columns[i] = append(columns[i], "  ")
 | 
						|
		}
 | 
						|
	}
 | 
						|
	//fmt.Println(max, "\n")
 | 
						|
	for x := 0; x < max; x++ {
 | 
						|
		for y := 0; y < total; y++ {
 | 
						|
 | 
						|
			// Check if border is set
 | 
						|
			if !t.noWhiteSpace {
 | 
						|
				fmt.Fprint(t.out, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
 | 
						|
				fmt.Fprintf(t.out, SPACE)
 | 
						|
			}
 | 
						|
 | 
						|
			str := columns[y][x]
 | 
						|
 | 
						|
			// Embedding escape sequence with column value
 | 
						|
			if is_esc_seq {
 | 
						|
				str = format(str, t.columnsParams[y])
 | 
						|
			}
 | 
						|
 | 
						|
			// This would print alignment
 | 
						|
			// Default alignment  would use multiple configuration
 | 
						|
			switch t.columnsAlign[y] {
 | 
						|
			case ALIGN_CENTER: //
 | 
						|
				fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
 | 
						|
			case ALIGN_RIGHT:
 | 
						|
				fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
 | 
						|
			case ALIGN_LEFT:
 | 
						|
				fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
 | 
						|
			default:
 | 
						|
				if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
 | 
						|
					fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
 | 
						|
				} else {
 | 
						|
					fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
 | 
						|
 | 
						|
					// TODO Custom alignment per column
 | 
						|
					//if max == 1 || pads[y] > 0 {
 | 
						|
					//	fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
 | 
						|
					//} else {
 | 
						|
					//	fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
 | 
						|
					//}
 | 
						|
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if !t.noWhiteSpace {
 | 
						|
				fmt.Fprintf(t.out, SPACE)
 | 
						|
			} else {
 | 
						|
				fmt.Fprintf(t.out, t.tablePadding)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		// Check if border is set
 | 
						|
		// Replace with space if not set
 | 
						|
		if !t.noWhiteSpace {
 | 
						|
			fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
 | 
						|
		}
 | 
						|
		fmt.Fprint(t.out, t.newLine)
 | 
						|
	}
 | 
						|
 | 
						|
	if t.rowLine {
 | 
						|
		t.printLine(true)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Print the rows of the table and merge the cells that are identical
 | 
						|
func (t *Table) printRowsMergeCells() {
 | 
						|
	var previousLine []string
 | 
						|
	var displayCellBorder []bool
 | 
						|
	var tmpWriter bytes.Buffer
 | 
						|
	for i, lines := range t.lines {
 | 
						|
		// We store the display of the current line in a tmp writer, as we need to know which border needs to be print above
 | 
						|
		previousLine, displayCellBorder = t.printRowMergeCells(&tmpWriter, lines, i, previousLine)
 | 
						|
		if i > 0 { //We don't need to print borders above first line
 | 
						|
			if t.rowLine {
 | 
						|
				t.printLineOptionalCellSeparators(true, displayCellBorder)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		tmpWriter.WriteTo(t.out)
 | 
						|
	}
 | 
						|
	//Print the end of the table
 | 
						|
	if t.rowLine {
 | 
						|
		t.printLine(true)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Print Row Information to a writer and merge identical cells.
 | 
						|
// Adjust column alignment based on type
 | 
						|
 | 
						|
func (t *Table) printRowMergeCells(writer io.Writer, columns [][]string, rowIdx int, previousLine []string) ([]string, []bool) {
 | 
						|
	// Get Maximum Height
 | 
						|
	max := t.rs[rowIdx]
 | 
						|
	total := len(columns)
 | 
						|
 | 
						|
	// Pad Each Height
 | 
						|
	pads := []int{}
 | 
						|
 | 
						|
	// Checking for ANSI escape sequences for columns
 | 
						|
	is_esc_seq := false
 | 
						|
	if len(t.columnsParams) > 0 {
 | 
						|
		is_esc_seq = true
 | 
						|
	}
 | 
						|
	for i, line := range columns {
 | 
						|
		length := len(line)
 | 
						|
		pad := max - length
 | 
						|
		pads = append(pads, pad)
 | 
						|
		for n := 0; n < pad; n++ {
 | 
						|
			columns[i] = append(columns[i], "  ")
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	var displayCellBorder []bool
 | 
						|
	t.fillAlignment(total)
 | 
						|
	for x := 0; x < max; x++ {
 | 
						|
		for y := 0; y < total; y++ {
 | 
						|
 | 
						|
			// Check if border is set
 | 
						|
			fmt.Fprint(writer, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
 | 
						|
 | 
						|
			fmt.Fprintf(writer, SPACE)
 | 
						|
 | 
						|
			str := columns[y][x]
 | 
						|
 | 
						|
			// Embedding escape sequence with column value
 | 
						|
			if is_esc_seq {
 | 
						|
				str = format(str, t.columnsParams[y])
 | 
						|
			}
 | 
						|
 | 
						|
			if t.autoMergeCells {
 | 
						|
				//Store the full line to merge mutli-lines cells
 | 
						|
				fullLine := strings.TrimRight(strings.Join(columns[y], " "), " ")
 | 
						|
				if len(previousLine) > y && fullLine == previousLine[y] && fullLine != "" {
 | 
						|
					// If this cell is identical to the one above but not empty, we don't display the border and keep the cell empty.
 | 
						|
					displayCellBorder = append(displayCellBorder, false)
 | 
						|
					str = ""
 | 
						|
				} else {
 | 
						|
					// First line or different content, keep the content and print the cell border
 | 
						|
					displayCellBorder = append(displayCellBorder, true)
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			// This would print alignment
 | 
						|
			// Default alignment  would use multiple configuration
 | 
						|
			switch t.columnsAlign[y] {
 | 
						|
			case ALIGN_CENTER: //
 | 
						|
				fmt.Fprintf(writer, "%s", Pad(str, SPACE, t.cs[y]))
 | 
						|
			case ALIGN_RIGHT:
 | 
						|
				fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
 | 
						|
			case ALIGN_LEFT:
 | 
						|
				fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
 | 
						|
			default:
 | 
						|
				if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
 | 
						|
					fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
 | 
						|
				} else {
 | 
						|
					fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
 | 
						|
				}
 | 
						|
			}
 | 
						|
			fmt.Fprintf(writer, SPACE)
 | 
						|
		}
 | 
						|
		// Check if border is set
 | 
						|
		// Replace with space if not set
 | 
						|
		fmt.Fprint(writer, ConditionString(t.borders.Left, t.pColumn, SPACE))
 | 
						|
		fmt.Fprint(writer, t.newLine)
 | 
						|
	}
 | 
						|
 | 
						|
	//The new previous line is the current one
 | 
						|
	previousLine = make([]string, total)
 | 
						|
	for y := 0; y < total; y++ {
 | 
						|
		previousLine[y] = strings.TrimRight(strings.Join(columns[y], " "), " ") //Store the full line for multi-lines cells
 | 
						|
	}
 | 
						|
	//Returns the newly added line and wether or not a border should be displayed above.
 | 
						|
	return previousLine, displayCellBorder
 | 
						|
}
 | 
						|
 | 
						|
func (t *Table) parseDimension(str string, colKey, rowKey int) []string {
 | 
						|
	var (
 | 
						|
		raw      []string
 | 
						|
		maxWidth int
 | 
						|
	)
 | 
						|
 | 
						|
	raw = getLines(str)
 | 
						|
	maxWidth = 0
 | 
						|
	for _, line := range raw {
 | 
						|
		if w := DisplayWidth(line); w > maxWidth {
 | 
						|
			maxWidth = w
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// If wrapping, ensure that all paragraphs in the cell fit in the
 | 
						|
	// specified width.
 | 
						|
	if t.autoWrap {
 | 
						|
		// If there's a maximum allowed width for wrapping, use that.
 | 
						|
		if maxWidth > t.mW {
 | 
						|
			maxWidth = t.mW
 | 
						|
		}
 | 
						|
 | 
						|
		// In the process of doing so, we need to recompute maxWidth. This
 | 
						|
		// is because perhaps a word in the cell is longer than the
 | 
						|
		// allowed maximum width in t.mW.
 | 
						|
		newMaxWidth := maxWidth
 | 
						|
		newRaw := make([]string, 0, len(raw))
 | 
						|
 | 
						|
		if t.reflowText {
 | 
						|
			// Make a single paragraph of everything.
 | 
						|
			raw = []string{strings.Join(raw, " ")}
 | 
						|
		}
 | 
						|
		for i, para := range raw {
 | 
						|
			paraLines, _ := WrapString(para, maxWidth)
 | 
						|
			for _, line := range paraLines {
 | 
						|
				if w := DisplayWidth(line); w > newMaxWidth {
 | 
						|
					newMaxWidth = w
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if i > 0 {
 | 
						|
				newRaw = append(newRaw, " ")
 | 
						|
			}
 | 
						|
			newRaw = append(newRaw, paraLines...)
 | 
						|
		}
 | 
						|
		raw = newRaw
 | 
						|
		maxWidth = newMaxWidth
 | 
						|
	}
 | 
						|
 | 
						|
	// Store the new known maximum width.
 | 
						|
	v, ok := t.cs[colKey]
 | 
						|
	if !ok || v < maxWidth || v == 0 {
 | 
						|
		t.cs[colKey] = maxWidth
 | 
						|
	}
 | 
						|
 | 
						|
	// Remember the number of lines for the row printer.
 | 
						|
	h := len(raw)
 | 
						|
	v, ok = t.rs[rowKey]
 | 
						|
 | 
						|
	if !ok || v < h || v == 0 {
 | 
						|
		t.rs[rowKey] = h
 | 
						|
	}
 | 
						|
	//fmt.Printf("Raw %+v %d\n", raw, len(raw))
 | 
						|
	return raw
 | 
						|
}
 |