Skip to content

Commit

Permalink
Logos: Add extra hyperlink option
Browse files Browse the repository at this point in the history
When exporting to Logos, you can pass
-Dbiblemulticonverter.logos.extralinkfiles which points to a list of CSV
files. First column is a Strongs number with prefix, second column are
optional conditions (space separated) which can either be verse numbers
in OSIS format like Gen.2.3, RMAC codes or other Strong numbers, or a
combination of them separated by "+". Gen.2.3+CONJ would match
conjunction in Gen 2:3. Column 3 and further columns are Logos
hyperlinks (without spaces) to add to the Strongs number. Use a link of
"-" to remove the Strongs number from the links. There may be multiple
matches (in multiple files or in the same file) for a Strongs number.

Any Logos links (lemma, Louw Nida, Goodrick-Kohlenberger, ...) are
supported.
  • Loading branch information
schierlm committed Dec 20, 2023
1 parent 313b0c7 commit 9a62486
Showing 1 changed file with 166 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -35,6 +38,8 @@
import biblemulticonverter.data.Utils;
import biblemulticonverter.data.Verse;
import biblemulticonverter.data.VerseRange;
import biblemulticonverter.data.Versification;
import biblemulticonverter.data.Versification.Reference;
import biblemulticonverter.format.AbstractHTMLVisitor;
import biblemulticonverter.format.ExportFormat;
import biblemulticonverter.logos.tools.LogosVersificationDetector;
Expand Down Expand Up @@ -192,6 +197,7 @@ public class LogosHTML implements ExportFormat {
private int footnoteNumber = 0;
private int grammarCounter = 0;
private String lineSeparator;
private Map<String, List<ExtraLinkRule>> extraLinkRules;

@Override
public void doExport(Bible bible, String... exportArgs) throws Exception {
Expand Down Expand Up @@ -232,6 +238,56 @@ public void doExport(Bible bible, String... exportArgs) throws Exception {
}
schemes.put(xrefVersemap, xrefScheme);
}
extraLinkRules = new HashMap<>();
for (String extraLinkFile : System.getProperty("biblemulticonverter.logos.extralinkfiles", "").split(File.pathSeparator)) {
if (extraLinkFile.isEmpty())
continue;
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(extraLinkFile), StandardCharsets.UTF_8))) {
String line;
while ((line = br.readLine()) != null) {
String[] fields = line.split("[\t,;]");
if (fields.length < 3) {
System.out.println("WARNING: ExtraLinkFile " + extraLinkFile + " incomplete line: " + line);
continue;
}
if (!fields[0].matches("[A-Z][0-9]+")) {
System.out.println("WARNING: ExtraLinkFile " + extraLinkFile + " skipping invalid Strong number: " + line);
continue;
}
List<ExtraLinkCondition> conditions = null;
if (fields[1].length() > 1) {
conditions = new ArrayList<>();
for (String cond : fields[1].split(" ")) {
ExtraLinkCondition c = ExtraLinkCondition.parse(cond.split("\\+"));
if (c == null) {
System.out.println("WARNING: ExtraLinkFile " + extraLinkFile + " skipping invalid condition " + cond + " in: " + line);
} else {
conditions.add(c);
}
}
if (conditions.isEmpty()) {
System.out.println("WARNING: ExtraLinkFile " + extraLinkFile + " skipping line without valid conditions: " + line);
}
}
boolean skipStrongs = false;
List<String> links = new ArrayList<String>();
for (int i = 2; i < fields.length; i++) {
if (fields[i].contains(":") && !fields[i].contains(" ")) {
links.add(fields[i]);
} else if (fields[i].equals("-")) {
skipStrongs = true;
} else {
System.out.println("WARNING: ExtraLinkFile " + extraLinkFile + " skipping invalid link " + fields[i] + " in: " + line);
}
}
if (links.isEmpty()) {
System.out.println("WARNING: ExtraLinkFile " + extraLinkFile + " skipping line with no links: " + line);
continue;
}
extraLinkRules.computeIfAbsent(fields[0], x -> new ArrayList<>()).add(new ExtraLinkRule(conditions, links, skipStrongs));
}
}
}
footnoteCounter = 0;
grammarCounter = 0;
String title = bible.getName();
Expand Down Expand Up @@ -320,7 +376,7 @@ public void doExport(Bible bible, String... exportArgs) throws Exception {
}
bw.write(book.getLongName() + "</h" + bookHeadlineLevel + ">\n");
footnoteNumber = 0;
chapter.getProlog().accept(new LogosVisitor(bw, "", footnotes, false, versemap, schemes, null, null, null, 2, null));
chapter.getProlog().accept(new LogosVisitor(bw, "", footnotes, false, null, versemap, schemes, null, null, null, 2, null));
bw.write("\n<br/>\n");
continue;
}
Expand All @@ -344,18 +400,18 @@ public void doExport(Bible bible, String... exportArgs) throws Exception {
vv.finished();
prologue.getVerses().add(vv);
}
exportChapter(milestone, "Prologue", prologue, versemap, schemes, verseSeparator, bookHeadlineLevel, noChapterHeadings, bw, footnotes, book, chapterVerses, prologueVerses);
exportChapter(milestone, 0, "Prologue", prologue, versemap, schemes, verseSeparator, bookHeadlineLevel, noChapterHeadings, bw, footnotes, book, chapterVerses, prologueVerses);
}
BitSet thisChapterVerses = chapterVerses != null && cnumber <= chapterVerses.length ? chapterVerses[cnumber - 1] : null;
exportChapter(milestone, "" + cnumber, chapter, versemap, schemes, verseSeparator, bookHeadlineLevel, noChapterHeadings, bw, footnotes, book, chapterVerses, thisChapterVerses);
exportChapter(milestone, cnumber, "" + cnumber, chapter, versemap, schemes, verseSeparator, bookHeadlineLevel, noChapterHeadings, bw, footnotes, book, chapterVerses, thisChapterVerses);
}
}
bw.write(footnotes.toString());
bw.write("</body></html>");
}
}

protected void exportChapter(String milestone, String cname, Chapter chapter, String versemap, LinkedHashMap<String,VersificationScheme> schemes, String verseSeparator, int bookHeadlineLevel, boolean noChapterHeadings, BufferedWriter bw, StringWriter footnotes, Book book, BitSet[] chapterVerses, BitSet thisChapterVerses) throws IOException {
protected void exportChapter(String milestone, int cnumber, String cname, Chapter chapter, String versemap, LinkedHashMap<String,VersificationScheme> schemes, String verseSeparator, int bookHeadlineLevel, boolean noChapterHeadings, BufferedWriter bw, StringWriter footnotes, Book book, BitSet[] chapterVerses, BitSet thisChapterVerses) throws IOException {
String chapterRef = "@" + formatMilestone(milestone, cname, "");
boolean writeChapterNumber = false;
int usedHeadlines = bookHeadlineLevel;
Expand All @@ -370,7 +426,7 @@ protected void exportChapter(String milestone, String cname, Chapter chapter, St
footnoteNumber = 0;
String[] verseNumbersToSkip = System.getProperty("biblemulticonverter.logos.skipversenumbers", "").split(",");
if (chapter.getProlog() != null) {
chapter.getProlog().accept(new LogosVisitor(bw, "", footnotes, book.getId().isNT(), versemap, schemes, null, null, null, usedHeadlines, null));
chapter.getProlog().accept(new LogosVisitor(bw, "", footnotes, book.getId().isNT(), null, versemap, schemes, null, null, null, usedHeadlines, null));
bw.write("\n<br/>\n");
}
Chapter verseChapter = chapter;
Expand Down Expand Up @@ -437,7 +493,8 @@ protected void exportChapter(String milestone, String cname, Chapter chapter, St
break;
}
}
v.accept(new LogosVisitor(bw, "", footnotes, book.getId().isNT(), versemap, schemes, versePrefix + verseNumber, versePrefixBeforeHeadline, versePrefixAfterHeadline + verseNumber, usedHeadlines, formatMilestone(milestone, "%c", "")));
int cn = vr.getChapter() == 0 ? cnumber : vr.getChapter();
v.accept(new LogosVisitor(bw, "", footnotes, book.getId().isNT(), cn == 0 ? null : new Reference(book.getId(), cn, v.getNumber()), versemap, schemes, versePrefix + verseNumber, versePrefixBeforeHeadline, versePrefixAfterHeadline + verseNumber, usedHeadlines, formatMilestone(milestone, "%c", "")));
versePrefix = "";
versePrefixBeforeHeadline = "";
versePrefixAfterHeadline = "";
Expand Down Expand Up @@ -579,6 +636,7 @@ private class LogosVisitor extends AbstractHTMLVisitor {

private StringWriter footnoteWriter;
private boolean nt;
private final Reference verseReference;
private String versemap;
private final LinkedHashMap<String,VersificationScheme> schemes;
private boolean grammarFlag;
Expand All @@ -589,10 +647,11 @@ private class LogosVisitor extends AbstractHTMLVisitor {
private final int usedHeadlines;
private final String currentBookMilestone;

protected LogosVisitor(Writer writer, String suffix, StringWriter footnoteWriter, boolean nt, String versemap, LinkedHashMap<String,VersificationScheme> schemes, String fieldPrefix, String fieldPrefixBeforeHeadline, String fieldPrefixAfterHeadline, int usedHeadlines, String currentBookMilestone) {
protected LogosVisitor(Writer writer, String suffix, StringWriter footnoteWriter, boolean nt, Reference verseReference, String versemap, LinkedHashMap<String, VersificationScheme> schemes, String fieldPrefix, String fieldPrefixBeforeHeadline, String fieldPrefixAfterHeadline, int usedHeadlines, String currentBookMilestone) {
super(writer, suffix);
this.footnoteWriter = footnoteWriter;
this.nt = nt;
this.verseReference = verseReference;
this.versemap = versemap;
this.schemes = schemes;
this.fieldPrefix = fieldPrefix;
Expand Down Expand Up @@ -661,7 +720,7 @@ public Visitor<IOException> visitFootnote() throws IOException {

footnoteWriter.write("<DIV ID=\"sdfootnote" + footnoteCounter + "\">");
writer.write("<A CLASS=\"sdfootnoteanc\" HREF=\"#sdfootnote" + footnoteCounter + "sym\" sdfixed><sup>" + footnoteNumber + "</sup></A>");
return new LogosVisitor(footnoteWriter, "</DIV>\n", null, nt, versemap, schemes, null, null, null, usedHeadlines, null);
return new LogosVisitor(footnoteWriter, "</DIV>\n", null, nt, verseReference, versemap, schemes, null, null, null, usedHeadlines, null);
}

@Override
Expand Down Expand Up @@ -731,16 +790,40 @@ public Visitor<IOException> visitDictionaryEntry(String dictionary, String entry
@Override
public Visitor<IOException> visitGrammarInformation(char[] strongsPrefixes, int[] strongs, String[] rmac, int[] sourceIndices) throws IOException {
List<String> links = new ArrayList<String>();
String[] expandedStrongs = strongs == null ? null : new String[strongs.length];
if (strongs != null) {
for (int i = 0; i < strongs.length; i++) {
expandedStrongs[i] = (strongsPrefixes == null ? (nt ? 'G' : 'H') : strongsPrefixes[i]) + "" + strongs[i];
}
}
int max = Math.max(strongs == null ? 0 : strongs.length, rmac == null ? 0 : rmac.length);
for (int i = 0; i < max; i++) {
boolean useNT = nt;
if (strongsPrefixes != null && i < strongs.length && strongsPrefixes[i] == 'G')
useNT = true;
else if (strongsPrefixes != null && i < strongs.length && strongsPrefixes[i] == 'H')
useNT = false;
String type = useNT ? "GreekStrongs:G" : "HebrewStrongs:H";
if (strongs != null && i < strongs.length)
links.add(type + strongs[i]);
if (strongs != null && i < strongs.length) {
boolean skipStrongs = false;
for (ExtraLinkRule r : extraLinkRules.getOrDefault(expandedStrongs[i], Collections.emptyList())) {
if (r.conditions != null) {
boolean conditionFound = false;
for(ExtraLinkCondition cond: r.conditions) {
if (cond.matches(verseReference, expandedStrongs, rmac)) {
conditionFound = true;
break;
}
}
if (!conditionFound) continue;
}
skipStrongs |= r.skipStrongs;
links.addAll(r.links);
}
if (!skipStrongs) {
boolean useNT = nt;
if (expandedStrongs[i].charAt(0) == 'G')
useNT = true;
else if (expandedStrongs[i].charAt(0) == 'H')
useNT = false;
String type = useNT ? "GreekStrongs:G" : "HebrewStrongs:H";
links.add(type + strongs[i]);
}
}
if (rmac != null && i < rmac.length)
links.add("LogosMorphGr:" + convertMorphology(rmac[i]));
}
Expand Down Expand Up @@ -836,4 +919,71 @@ public boolean visitEnd() throws IOException {
}

}

private static class ExtraLinkRule {
private final List<ExtraLinkCondition> conditions;
private final List<String> links;
private final boolean skipStrongs;

public ExtraLinkRule(List<ExtraLinkCondition> conditions, List<String> links, boolean skipStrongs) {
super();
this.conditions = conditions;
this.links = links;
this.skipStrongs = skipStrongs;
}
}

private static class ExtraLinkCondition {
private final Versification.Reference verseNumber;
private final List<String> strongNumbers;
private final List<String> rmacNumbers;

public static ExtraLinkCondition parse(String[] conditions) {
Reference verseNumber = null;
final List<String> strongNumbers = new ArrayList<>();
final List<String> rmacNumbers = new ArrayList<>();
for (String cond : conditions) {
try {
if (cond.matches(".*\\.[0-9]+\\." + Utils.VERSE_REGEX)) {
String[] parts = cond.split("\\.", 3);
if (verseNumber != null)
throw new IllegalArgumentException("More than one verse reference");
verseNumber = new Versification.Reference(BookID.fromOsisId(parts[0]), Integer.parseInt(parts[1]), parts[2]);
} else if (cond.matches("[A-Z][0-9]+")) {
strongNumbers.add(cond);
} else if (cond.matches(Utils.RMAC_REGEX)) {
rmacNumbers.add(cond);
} else {
throw new IllegalArgumentException("Unsupported condition format");
}
} catch (IllegalArgumentException ex) {
System.out.println("WARNING: Invalid extra link condition " + cond + ": " + ex.toString());
return null;
}
}
return new ExtraLinkCondition(verseNumber, strongNumbers, rmacNumbers);
}

private ExtraLinkCondition(Reference verseNumber, List<String> strongNumbers, List<String> rmacNumbers) {
super();
this.verseNumber = verseNumber;
this.strongNumbers = strongNumbers;
this.rmacNumbers = rmacNumbers;
}

public boolean matches(Reference verseNum, String[] strongs, String[] rmacs) {
if (verseNumber != null && !verseNumber.equals(verseNum))
return false;
for (String strong : strongNumbers) {
if (strongs == null || !Arrays.asList(strongs).contains(strong))
return false;
}
for (String rmac : rmacNumbers) {
if (rmacs == null || !Arrays.asList(rmacs).contains(rmac)) {
return false;
}
}
return true;
}
}
}

0 comments on commit 9a62486

Please sign in to comment.