From ebb527596ec4dad842232cf8b30cb1d48f34244d Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Wed, 24 May 2017 23:01:19 +0100 Subject: Write thead and th for headers in table --- .../internal/conversion/DocumentToHtml.java | 122 ++++++++++++++------- 1 file changed, 85 insertions(+), 37 deletions(-) (limited to 'src/main/java/org/zwobble/mammoth/internal/conversion/DocumentToHtml.java') diff --git a/src/main/java/org/zwobble/mammoth/internal/conversion/DocumentToHtml.java b/src/main/java/org/zwobble/mammoth/internal/conversion/DocumentToHtml.java index bdb3d01..9de8224 100644 --- a/src/main/java/org/zwobble/mammoth/internal/conversion/DocumentToHtml.java +++ b/src/main/java/org/zwobble/mammoth/internal/conversion/DocumentToHtml.java @@ -15,6 +15,8 @@ import java.io.InputStream; import java.util.*; import java.util.function.Supplier; +import static org.zwobble.mammoth.internal.util.Casts.tryCast; +import static org.zwobble.mammoth.internal.util.Iterables.findIndex; import static org.zwobble.mammoth.internal.util.Lists.*; import static org.zwobble.mammoth.internal.util.Maps.lookup; import static org.zwobble.mammoth.internal.util.Maps.map; @@ -23,7 +25,7 @@ public class DocumentToHtml { public static InternalResult> convertToHtml(Document document, DocumentToHtmlOptions options) { DocumentToHtml documentConverter = new DocumentToHtml(options, document.getComments()); return new InternalResult<>( - documentConverter.convertToHtml(document), + documentConverter.convertToHtml(document, new Context()), documentConverter.warnings); } @@ -37,7 +39,7 @@ public class DocumentToHtml { public static InternalResult> convertToHtml(DocumentElement element, DocumentToHtmlOptions options) { DocumentToHtml documentConverter = new DocumentToHtml(options, list()); return new InternalResult<>( - documentConverter.convertToHtml(element), + documentConverter.convertToHtml(element, new Context()), documentConverter.warnings); } @@ -60,6 +62,22 @@ public class DocumentToHtml { private final List referencedComments = new ArrayList<>(); private final Set warnings = new HashSet<>(); + private static class Context { + private final boolean isHeader; + + Context() { + this(false); + } + + Context(boolean isHeader) { + this.isHeader = isHeader; + } + + Context isHeader(boolean isHeader) { + return new Context(isHeader); + } + } + private DocumentToHtml(DocumentToHtmlOptions options, List comments) { this.idPrefix = options.idPrefix(); this.preserveEmptyParagraphs = options.shouldPreserveEmptyParagraphs(); @@ -68,26 +86,26 @@ public class DocumentToHtml { this.comments = Maps.toMapWithKey(comments, Comment::getCommentId); } - private List convertToHtml(Document document) { - List mainBody = convertChildrenToHtml(document); + private List convertToHtml(Document document, Context context) { + List mainBody = convertChildrenToHtml(document, context); // TODO: can you have note references inside a note? List notes = findNotes(document, noteReferences); List noteNodes = notes.isEmpty() ? list() - : list(Html.element("ol", eagerMap(notes, this::convertToHtml))); + : list(Html.element("ol", eagerMap(notes, note -> convertToHtml(note, context)))); List commentNodes = referencedComments.isEmpty() ? list() - : list(Html.element("dl", eagerFlatMap(referencedComments, this::convertToHtml))); + : list(Html.element("dl", eagerFlatMap(referencedComments, comment -> convertToHtml(comment, context)))); return eagerConcat(mainBody, noteNodes, commentNodes); } - private HtmlNode convertToHtml(Note note) { + private HtmlNode convertToHtml(Note note, Context context) { String id = generateNoteHtmlId(note.getNoteType(), note.getId()); String referenceId = generateNoteRefHtmlId(note.getNoteType(), note.getId()); - List noteBody = convertToHtml(note.getBody()); + List noteBody = convertToHtml(note.getBody(), context); // TODO: we probably want this to collapse more eagerly than other collapsible elements // -- for instance, any paragraph will probably do, regardless of attributes. (Possible other elements will do too.) HtmlNode backLink = Html.collapsibleElement("p", list( @@ -96,10 +114,10 @@ public class DocumentToHtml { return Html.element("li", map("id", id), eagerConcat(noteBody, list(backLink))); } - private List convertToHtml(ReferencedComment referencedComment) { + private List convertToHtml(ReferencedComment referencedComment, Context context) { // TODO: remove duplication with notes String commentId = referencedComment.comment.getCommentId(); - List body = convertToHtml(referencedComment.comment.getBody()); + List body = convertToHtml(referencedComment.comment.getBody(), context); // TODO: we probably want this to collapse more eagerly than other collapsible elements // -- for instance, any paragraph will probably do, regardless of attributes. (Possible other elements will do too.) HtmlNode backLink = Html.collapsibleElement("p", list( @@ -115,22 +133,23 @@ public class DocumentToHtml { eagerConcat(body, list(backLink)))); } - private List convertToHtml(List elements) { + private List convertToHtml(List elements, Context context) { return eagerFlatMap( elements, - this::convertToHtml); + element -> convertToHtml(element, context) + ); } - private List convertChildrenToHtml(HasChildren element) { - return convertToHtml(element.getChildren()); + private List convertChildrenToHtml(HasChildren element, Context context) { + return convertToHtml(element.getChildren(), context); } - private List convertToHtml(DocumentElement element) { - return element.accept(new DocumentElementVisitor>() { + private List convertToHtml(DocumentElement element, Context context) { + return element.accept(new DocumentElementVisitor, Context>() { @Override - public List visit(Paragraph paragraph) { + public List visit(Paragraph paragraph, Context context) { Supplier> children = () -> { - List content = convertChildrenToHtml(paragraph); + List content = convertChildrenToHtml(paragraph, context); return preserveEmptyParagraphs ? cons(Html.FORCE_WRITE, content) : content; }; HtmlPath mapping = styleMap.getParagraphHtmlPath(paragraph) @@ -144,8 +163,8 @@ public class DocumentToHtml { } @Override - public List visit(Run run) { - Supplier> nodes = () -> convertChildrenToHtml(run); + public List visit(Run run, Context context) { + Supplier> nodes = () -> convertChildrenToHtml(run, context); if (run.isStrikethrough()) { nodes = styleMap.getStrikethrough().orElse(HtmlPath.collapsibleElement("s")).wrap(nodes); } @@ -175,7 +194,7 @@ public class DocumentToHtml { } @Override - public List visit(Text text) { + public List visit(Text text, Context context) { if (text.getValue().isEmpty()) { return list(); } else { @@ -184,12 +203,12 @@ public class DocumentToHtml { } @Override - public List visit(Tab tab) { + public List visit(Tab tab, Context context) { return list(Html.text("\t")); } @Override - public List visit(Break breakElement) { + public List visit(Break breakElement, Context context) { HtmlPath mapping = styleMap.getBreakHtmlPath(breakElement) .orElseGet(() -> { if (breakElement.getType() == Break.Type.LINE) { @@ -202,17 +221,46 @@ public class DocumentToHtml { } @Override - public List visit(Table table) { - return list(Html.element("table", convertChildrenToHtml(table))); + public List visit(Table table, Context context) { + List children = generateTableChildren(table, context); + return list(Html.element("table", children)); + } + + private List generateTableChildren(Table table, Context context) { + int bodyIndex = findIndex(table.getChildren(), child -> !isHeader(child)) + .orElse(table.getChildren().size()); + if (bodyIndex == 0) { + return convertToHtml(table.getChildren(), context.isHeader(false)); + } else { + List headRows = convertToHtml( + table.getChildren().subList(0, bodyIndex), + context.isHeader(true) + ); + List bodyRows = convertToHtml( + table.getChildren().subList(bodyIndex, table.getChildren().size()), + context.isHeader(false) + ); + return list( + Html.element("thead", headRows), + Html.element("tbody", bodyRows) + ); + } + } + + private boolean isHeader(DocumentElement child) { + return tryCast(TableRow.class, child) + .map(TableRow::isHeader) + .orElse(false); } @Override - public List visit(TableRow tableRow) { - return list(Html.element("tr", convertChildrenToHtml(tableRow))); + public List visit(TableRow tableRow, Context context) { + return list(Html.element("tr", convertChildrenToHtml(tableRow, context))); } @Override - public List visit(TableCell tableCell) { + public List visit(TableCell tableCell, Context context) { + String tagName = context.isHeader ? "th" : "td"; Map attributes = new HashMap<>(); if (tableCell.getColspan() != 1) { attributes.put("colspan", Integer.toString(tableCell.getColspan())); @@ -220,14 +268,14 @@ public class DocumentToHtml { if (tableCell.getRowspan() != 1) { attributes.put("rowspan", Integer.toString(tableCell.getRowspan())); } - return list(Html.element("td", attributes, - Lists.cons(Html.FORCE_WRITE, convertChildrenToHtml(tableCell)))); + return list(Html.element(tagName, attributes, + Lists.cons(Html.FORCE_WRITE, convertChildrenToHtml(tableCell, context)))); } @Override - public List visit(Hyperlink hyperlink) { + public List visit(Hyperlink hyperlink, Context context) { Map attributes = map("href", generateHref(hyperlink)); - return list(Html.collapsibleElement("a", attributes, convertChildrenToHtml(hyperlink))); + return list(Html.collapsibleElement("a", attributes, convertChildrenToHtml(hyperlink, context))); } private String generateHref(Hyperlink hyperlink) { @@ -241,12 +289,12 @@ public class DocumentToHtml { } @Override - public List visit(Bookmark bookmark) { + public List visit(Bookmark bookmark, Context context) { return list(Html.element("a", map("id", generateId(bookmark.getName())), list(Html.FORCE_WRITE))); } @Override - public List visit(NoteReference noteReference) { + public List visit(NoteReference noteReference, Context context) { noteReferences.add(noteReference); String noteAnchor = generateNoteHtmlId(noteReference.getNoteType(), noteReference.getNoteId()); String noteReferenceAnchor = generateNoteRefHtmlId(noteReference.getNoteType(), noteReference.getNoteId()); @@ -256,7 +304,7 @@ public class DocumentToHtml { } @Override - public List visit(CommentReference commentReference) { + public List visit(CommentReference commentReference, Context context) { return styleMap.getCommentReference().orElse(HtmlPath.IGNORE).wrap(() -> { String commentId = commentReference.getCommentId(); Comment comment = lookup(comments, commentId) @@ -275,7 +323,7 @@ public class DocumentToHtml { } @Override - public List visit(Image image) { + public List visit(Image image, Context context) { // TODO: custom image handlers // TODO: handle empty content type return image.getContentType() @@ -306,7 +354,7 @@ public class DocumentToHtml { }) .orElse(list()); } - }); + }, context); } private String generateNoteHtmlId(NoteType noteType, String noteId) { -- cgit v1.2.3