/*
* Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
*/
package commonmark4cj.table
let TableBlockType: NodeType = "TableBlock"
let TableBodyType: NodeType = "TableBody"
let TableCellType: NodeType = "TableCell"
let TableHeadType: NodeType = "TableHead"
let TableRowType: NodeType = "TableRow"
public abstract class TableNodeRenderer <: NodeRenderer {
public override func getNodeTypes(): HashSet<NodeType> {
return HashSet<NodeType>([TableBlockType, TableHeadType, TableBodyType, TableRowType, TableCellType])
}
public override func render(node: Node): Unit {
if (node is TableBlock) {
renderBlock((node as TableBlock)())
} else if (node is TableHead) {
renderHead((node as TableHead)())
} else if (node is TableBody) {
renderBody((node as TableBody)())
} else if (node is TableRow) {
renderRow((node as TableRow)())
} else if (node is TableCell) {
renderCell((node as TableCell)())
}
}
protected func renderBlock(node: TableBlock): Unit
protected func renderHead(node: TableHead): Unit
protected func renderBody(node: TableBody): Unit
protected func renderRow(node: TableRow): Unit
protected func renderCell(node: TableCell): Unit
}
public class TableBlock <: CustomBlock {
public override func getNodeType(): NodeType {
return TableBlockType
}
}
public class TableBody <: CustomNode {
public override func getNodeType(): NodeType {
return TableBodyType
}
}
public class TableCell <: CustomNode {
private var header: Bool = false
private var alignment: ?Alignment = None
private var width: Int = 0
/**
* @return whether the cell is a header or not
*/
public func isHeader(): Bool {
return header
}
public func setHeader(header: Bool): Unit {
this.header = header
}
/**
* @return the cell alignment
*/
public func getAlignment(): ?Alignment {
return alignment
}
public func setAlignment(alignment: ?Alignment): Unit {
this.alignment = alignment
}
public func getWidth(): Int {
this.width
}
public func setWidth(width: Int): Unit {
this.width = width
}
public override func toStringAttributes(): String {
return "alignment:${alignment}, width:${width}"
}
public override func getNodeType(): NodeType {
return TableCellType
}
}
/**
* How the cell is aligned horizontally.
*/
public enum Alignment <: ToString {
| LEFT
| CENTER
| RIGHT
public func toString(): String {
match (this) {
case LEFT => "LEFT"
case CENTER => "CENTER"
case RIGHT => "RIGHT"
}
}
}
class TableCellInfo {
private let alignment: ?Alignment
private let width: Int
public func getAlignment(): ?Alignment {
return alignment
}
public func getWidth(): Int {
return width
}
public TableCellInfo(alignment: ?Alignment, width: Int) {
this.alignment = alignment
this.width = width
}
}
public class TableHead <: CustomNode {
public override func getNodeType(): NodeType {
return TableHeadType
}
}
public class TableRow <: CustomNode {
public override func getNodeType(): NodeType {
return TableRowType
}
}
public class TablesExtension <: ParserExtension & HtmlRendererExtension & TextContentRendererExtension {
private init() {
}
public static func create(): Extension {
return TablesExtension()
}
public func ext(parserBuilder: ParserBuilder): Unit {
parserBuilder.customBlockParserFactory(TableBlockFactory())
}
public func ext(rendererBuilder: HtmlRendererBuilder): Unit {
rendererBuilder.nodeRendererFactory({
context => TableHtmlNodeRenderer(context)
})
}
public func ext(rendererBuilder: TextContentRendererBuilder): Unit {
rendererBuilder.nodeRendererFactory({
context => TableTextContentNodeRenderer(context)
})
}
}
class TableHtmlNodeRenderer <: TableNodeRenderer {
private let context: HtmlNodeRendererContext
private let htmlWriter: HtmlWriter
public init(context: HtmlNodeRendererContext) {
this.htmlWriter = context.getWriter()
this.context = context
}
protected func renderBlock(tableBlock: TableBlock): Unit {
htmlWriter.line()
htmlWriter.tag("table", getAttributes(tableBlock, "table"))
renderChildren(tableBlock)
htmlWriter.tag("/table")
htmlWriter.line()
}
protected func renderHead(tableHead: TableHead): Unit {
htmlWriter.line()
htmlWriter.tag("thead", getAttributes(tableHead, "thead"))
renderChildren(tableHead)
htmlWriter.tag("/thead")
htmlWriter.line()
}
protected func renderBody(tableBody: TableBody): Unit {
htmlWriter.line()
htmlWriter.tag("tbody", getAttributes(tableBody, "tbody"))
renderChildren(tableBody)
htmlWriter.tag("/tbody")
htmlWriter.line()
}
protected func renderRow(tableRow: TableRow): Unit {
htmlWriter.line()
htmlWriter.tag("tr", getAttributes(tableRow, "tr"))
renderChildren(tableRow)
htmlWriter.tag("/tr")
htmlWriter.line()
}
protected func renderCell(tableCell: TableCell): Unit {
var tagName: String = "td"
if (tableCell.isHeader()) {
tagName = "th"
}
htmlWriter.line()
htmlWriter.tag(tagName, getCellAttributes(tableCell, tagName))
renderChildren(tableCell)
htmlWriter.tag("/" + tagName)
htmlWriter.line()
}
private func getAttributes(node: Node, tagName: String): HashMap<String, String> {
return context.extendAttributes(node, tagName, HashMap<String, String>())
}
private func getCellAttributes(tableCell: TableCell, tagName: String): HashMap<String, String> {
if (tableCell.getAlignment().isSome()) {
let singletonMap: HashMap<String, String> = HashMap<String, String>(
[("align", getAlignValue(tableCell.getAlignment()()))])
return context.extendAttributes(tableCell, tagName, singletonMap);
} else {
return context.extendAttributes(tableCell, tagName, HashMap<String, String>())
}
}
private static func getAlignValue(alignment: Alignment): String {
match (alignment) {
case LEFT => return "left"
case CENTER => return "center"
case RIGHT => return "right"
}
}
private func renderChildren(parent: Node): Unit {
var node: ?Node = parent.getFirstChild()
while (let Some(v) <- node) {
var next: ?Node = v.getNext()
context.render(node())
node = next
}
}
}
class TableTextContentNodeRenderer <: TableNodeRenderer {
private let context: TextContentNodeRendererContext
private let textContentWriter: TextContentWriter
public init(context: TextContentNodeRendererContext) {
this.textContentWriter = context.getWriter()
this.context = context
}
protected func renderBlock(tableBlock: TableBlock): Unit {
renderChildren(tableBlock)
if (tableBlock.getNext().isSome()) {
textContentWriter.write("\n")
}
}
protected func renderHead(tableHead: TableHead): Unit {
renderChildren(tableHead)
}
protected func renderBody(tableBody: TableBody): Unit {
renderChildren(tableBody)
}
protected func renderRow(tableRow: TableRow): Unit {
textContentWriter.line()
renderChildren(tableRow)
textContentWriter.line()
}
protected func renderCell(tableCell: TableCell): Unit {
renderChildren(tableCell)
textContentWriter.write("|")
textContentWriter.whitespace()
}
private func renderLastCell(tableCell: TableCell): Unit {
renderChildren(tableCell)
}
private func renderChildren(parent: Node): Unit {
var node: ?Node = parent.getFirstChild()
while (node.isSome()) {
var next: ?Node = node().getNext()
// For last cell in row, we dont render the delimiter.
if (node() is TableCell && next.isNone()) {
renderLastCell((node() as TableCell).getOrThrow())
} else {
context.render((node()))
}
node = next
}
}
}