From 9a3a8523419efa46ed46c3044a5078e34928a7ed Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 14 Feb 2016 18:37:42 +0000 Subject: Convert note references and notes to HTML Currently missing the back link --- .../org/zwobble/mammoth/DocumentConverter.java | 93 +++++++++++++++++++--- src/main/java/org/zwobble/mammoth/Mammoth.java | 2 +- .../java/org/zwobble/mammoth/documents/Note.java | 4 + .../zwobble/mammoth/documents/NoteReference.java | 8 ++ .../mammoth/tests/DocumentConverterTests.java | 31 ++++++-- 5 files changed, 121 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/main/java/org/zwobble/mammoth/DocumentConverter.java b/src/main/java/org/zwobble/mammoth/DocumentConverter.java index 1c42b0b..6f59cba 100644 --- a/src/main/java/org/zwobble/mammoth/DocumentConverter.java +++ b/src/main/java/org/zwobble/mammoth/DocumentConverter.java @@ -1,6 +1,8 @@ package org.zwobble.mammoth; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; import com.google.common.io.ByteStreams; import org.zwobble.mammoth.documents.*; import org.zwobble.mammoth.html.Html; @@ -16,23 +18,68 @@ import static org.zwobble.mammoth.util.MammothLists.list; import static org.zwobble.mammoth.util.MammothMaps.map; public class DocumentConverter { + public static List convertToHtml(String idPrefix, Document document) { + DocumentConverter documentConverter = new DocumentConverter(idPrefix); + List mainBody = documentConverter.convertChildrenToHtml(document); + // TODO: can you have note references inside a note? + List notes = findNotes(document); + if (notes.isEmpty()) { + return mainBody; + } else { + HtmlNode noteNode = Html.element("ol", + ImmutableList.copyOf(Iterables.transform(notes, documentConverter::convertToHtml))); + + return ImmutableList.copyOf(Iterables.concat(mainBody, list(noteNode))); + } + } + + private static List findNotes(Document document) { + Iterable noteReferences = Iterables.filter(descendants(document), NoteReference.class); + return ImmutableList.copyOf(Iterables.transform( + noteReferences, + // TODO: handle missing notes + reference -> document.getNotes().findNote(reference.getNoteType(), reference.getNoteId()).get())); + } + + private static Iterable descendants(HasChildren element) { + return Iterables.concat(Iterables.transform(element.getChildren(), DocumentConverter::descendantsAndSelf)); + } + + private static Iterable descendantsAndSelf(DocumentElement element) { + if (element instanceof HasChildren) { + HasChildren hasChildren = (HasChildren) element; + return Iterables.concat(descendants(hasChildren), list(element)); + } else { + return list(element); + } + } + + public static List convertToHtml(String idPrefix, DocumentElement element) { + return new DocumentConverter(idPrefix).convertToHtml(element); + } + private final String idPrefix; - public DocumentConverter(String idPrefix) { + private DocumentConverter(String idPrefix) { this.idPrefix = idPrefix; } - public List convertToHtml(Document document) { - return convertChildrenToHtml(document); + private HtmlNode convertToHtml(Note note) { + String id = generateNoteHtmlId(note.getNoteType(), note.getId()); + return Html.element("li", map("id", id), convertToHtml(note.getBody())); } - private List convertChildrenToHtml(HasChildren element) { + private List convertToHtml(List elements) { return eagerFlatMap( - element.getChildren(), + elements, this::convertToHtml); } - public List convertToHtml(DocumentElement element) { + private List convertChildrenToHtml(HasChildren element) { + return convertToHtml(element.getChildren()); + } + + private List convertToHtml(DocumentElement element) { return element.accept(new DocumentElementVisitor>() { @Override public List visit(Paragraph paragraph) { @@ -120,13 +167,14 @@ public class DocumentConverter { return list(Html.element("a", map("id", generateId(bookmark.getName())))); } - private String generateId(String bookmarkName) { - return idPrefix + "-" + bookmarkName; - } - @Override public List visit(NoteReference noteReference) { - throw new UnsupportedOperationException(); + String noteAnchor = generateNoteHtmlId(noteReference.getNoteType(), noteReference.getNoteId()); + String noteReferenceAnchor = generateNoteRefHtmlId(noteReference.getNoteType(), noteReference.getNoteId()); + return list(Html.element("sup", list( + Html.element("a", map("href", "#" + noteAnchor, "id", noteReferenceAnchor), list( + // TODO: increment note indices + Html.text("[1]")))))); } @Override @@ -154,4 +202,27 @@ public class DocumentConverter { } }); } + + private String generateNoteHtmlId(NoteType noteType, String noteId) { + return generateId(noteTypeToIdFragment(noteType) + "-" + noteId); + } + + private String generateNoteRefHtmlId(NoteType noteType, String noteId) { + return generateId(noteTypeToIdFragment(noteType) + "-ref-" + noteId); + } + + private String noteTypeToIdFragment(NoteType noteType) { + switch (noteType) { + case FOOTNOTE: + return "footnote"; + case ENDNOTE: + return "endnote"; + default: + throw new UnsupportedOperationException(); + } + } + + private String generateId(String bookmarkName) { + return idPrefix + "-" + bookmarkName; + } } diff --git a/src/main/java/org/zwobble/mammoth/Mammoth.java b/src/main/java/org/zwobble/mammoth/Mammoth.java index 2201bd2..79e15f7 100644 --- a/src/main/java/org/zwobble/mammoth/Mammoth.java +++ b/src/main/java/org/zwobble/mammoth/Mammoth.java @@ -33,7 +33,7 @@ public class Mammoth { // TODO: prefix String idPrefix = "document"; return reader.readElement(documentXml) - .map(nodes -> new DocumentConverter(idPrefix).convertToHtml(nodes)) + .map(nodes -> DocumentConverter.convertToHtml(idPrefix, nodes)) .map(Html::write); } catch (IOException e) { throw new UnsupportedOperationException("Should return a result of failure"); diff --git a/src/main/java/org/zwobble/mammoth/documents/Note.java b/src/main/java/org/zwobble/mammoth/documents/Note.java index da6f68f..75c28c6 100644 --- a/src/main/java/org/zwobble/mammoth/documents/Note.java +++ b/src/main/java/org/zwobble/mammoth/documents/Note.java @@ -20,4 +20,8 @@ public class Note { public String getId() { return id; } + + public List getBody() { + return body; + } } diff --git a/src/main/java/org/zwobble/mammoth/documents/NoteReference.java b/src/main/java/org/zwobble/mammoth/documents/NoteReference.java index 01fff95..11190c3 100644 --- a/src/main/java/org/zwobble/mammoth/documents/NoteReference.java +++ b/src/main/java/org/zwobble/mammoth/documents/NoteReference.java @@ -17,6 +17,14 @@ public class NoteReference implements DocumentElement { this.noteId = noteId; } + public NoteType getNoteType() { + return noteType; + } + + public String getNoteId() { + return noteId; + } + @Override public T accept(DocumentElementVisitor visitor) { return visitor.visit(this); diff --git a/src/test/java/org/zwobble/mammoth/tests/DocumentConverterTests.java b/src/test/java/org/zwobble/mammoth/tests/DocumentConverterTests.java index 17bcdf1..f5b30b2 100644 --- a/src/test/java/org/zwobble/mammoth/tests/DocumentConverterTests.java +++ b/src/test/java/org/zwobble/mammoth/tests/DocumentConverterTests.java @@ -35,7 +35,7 @@ public class DocumentConverterTests { @Test public void multipleParagraphsInDocumentAreConvertedToMultipleParagraphs() { assertThat( - documentConverter().convertToHtml(new Document(list( + convertToHtml(new Document(list( make(a(PARAGRAPH, with(CHILDREN, list(runWithText("Hello"))))), make(a(PARAGRAPH, with(CHILDREN, list(runWithText("there")))))), Notes.EMPTY)), @@ -141,6 +141,27 @@ public class DocumentConverterTests { deepEquals(list(Html.element("a", map("id", "doc-42-start"))))); } + @Test + public void noteReferencesAreConvertedToLinksToReferenceBodyAfterMainBody() { + Document document = new Document( + list(make(a(PARAGRAPH, with(CHILDREN, list( + runWithText("Knock knock"), + make(a(RUN, with(CHILDREN, list(new NoteReference(NoteType.FOOTNOTE, "4")))))))))), + new Notes(list(new Note(NoteType.FOOTNOTE, "4", list(paragraphWithText("Who's there?")))))); + + assertThat( + convertToHtml(document), + deepEquals(list( + Html.element("p", list( + Html.text("Knock knock"), + Html.element("sup", list( + Html.element("a", map("href", "#doc-42-footnote-4", "id", "doc-42-footnote-ref-4"), list(Html.text("[1]"))))))), + Html.element("ol", list( + Html.element("li", map("id", "doc-42-footnote-4"), list( + Html.element("p", list( + Html.text("Who's there?")))))))))); + } + @Test public void imagesAreConvertedToImageTagsWithDataUriByDefault() { Image image = new Image( @@ -163,11 +184,11 @@ public class DocumentConverterTests { contains(hasProperty("attributes", hasEntry("alt", "It's a hat")))); } - private List convertToHtml(DocumentElement element) { - return documentConverter().convertToHtml(element); + private List convertToHtml(Document document) { + return DocumentConverter.convertToHtml("doc-42", document); } - private DocumentConverter documentConverter() { - return new DocumentConverter("doc-42"); + private List convertToHtml(DocumentElement element) { + return DocumentConverter.convertToHtml("doc-42", element); } } -- cgit v1.2.3