Skip to content

Commit

Permalink
🐛 catch reference cycles
Browse files Browse the repository at this point in the history
  • Loading branch information
ebullient committed Jan 20, 2024
1 parent 0c3624d commit 3522b08
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 42 deletions.
73 changes: 46 additions & 27 deletions src/main/java/dev/ebullient/convert/tools/ParseState.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public static class ParseStateInfo {
boolean inHtmlTable;
boolean inMarkdownTable;
boolean inList;
int inFeatureType;
final String listIndent;
final String src;
final int page;
Expand Down Expand Up @@ -68,6 +69,20 @@ private ParseStateInfo setInMarkdownTable(boolean inTable) {
return this;
}

private ParseStateInfo setInFeatureType(int inFeatureType) {
this.inFeatureType = inFeatureType;
return this;
}

private ParseStateInfo setTheRest(ParseStateInfo prev) {
this.setInFootnotes(prev.inFootnotes)
.setInHtmlTable(prev.inHtmlTable)
.setInMarkdownTable(prev.inMarkdownTable)
.setInList(prev.inList)
.setInFeatureType(prev.inFeatureType);
return this;
}

private static ParseStateInfo srcAndPage(ParseStateInfo prev, String src, int page) {
if (prev == null) {
return new ParseState.ParseStateInfo(src, page);
Expand All @@ -76,32 +91,25 @@ private static ParseStateInfo srcAndPage(ParseStateInfo prev, String src, int pa
src == null ? prev.src : src,
page,
prev.listIndent)
.setInFootnotes(prev.inFootnotes)
.setInHtmlTable(prev.inHtmlTable)
.setInMarkdownTable(prev.inMarkdownTable)
.setInList(prev.inList);
.setTheRest(prev);
}

private static ParseStateInfo changePage(ParseStateInfo prev, int page) {
if (prev == null) {
throw new IllegalStateException("Page without source first?");
Tui.instance().errorf("Change Page called without someone setting the source first? %s");
return null;
}
return new ParseStateInfo(prev.src, page, prev.listIndent)
.setInFootnotes(prev.inFootnotes)
.setInHtmlTable(prev.inHtmlTable)
.setInMarkdownTable(prev.inMarkdownTable)
.setInList(prev.inList);
.setTheRest(prev);
}

private static ParseStateInfo inFootnotes(ParseStateInfo prev, boolean inFootnotes) {
if (prev == null) {
return new ParseStateInfo().setInFootnotes(inFootnotes);
}
return new ParseState.ParseStateInfo(prev.src, prev.page, prev.listIndent)
.setInFootnotes(inFootnotes)
.setInHtmlTable(prev.inHtmlTable)
.setInMarkdownTable(prev.inMarkdownTable)
.setInList(prev.inList);
.setTheRest(prev)
.setInFootnotes(inFootnotes);
}

private static ParseStateInfo inHtmlTable(ParseStateInfo prev, boolean inHtmlTable) {
Expand All @@ -110,10 +118,8 @@ private static ParseStateInfo inHtmlTable(ParseStateInfo prev, boolean inHtmlTab

}
return new ParseState.ParseStateInfo(prev.src, prev.page, prev.listIndent)
.setInFootnotes(prev.inFootnotes)
.setInHtmlTable(inHtmlTable)
.setInMarkdownTable(prev.inMarkdownTable)
.setInList(prev.inList);
.setTheRest(prev)
.setInHtmlTable(inHtmlTable);
}

private static ParseStateInfo inMarkdownTable(ParseStateInfo prev, boolean inMarkdownTable) {
Expand All @@ -122,20 +128,16 @@ private static ParseStateInfo inMarkdownTable(ParseStateInfo prev, boolean inMar

}
return new ParseState.ParseStateInfo(prev.src, prev.page, prev.listIndent)
.setInFootnotes(prev.inFootnotes)
.setInHtmlTable(prev.inHtmlTable)
.setInMarkdownTable(inMarkdownTable)
.setInList(prev.inList);
.setTheRest(prev)
.setInMarkdownTable(inMarkdownTable);
}

private static ParseStateInfo indentList(ParseStateInfo prev) {
if (prev == null) {
return new ParseStateInfo().setInList(true);
}
return new ParseState.ParseStateInfo(prev.src, prev.page, prev.listIndent + " ")
.setInFootnotes(prev.inFootnotes)
.setInHtmlTable(prev.inHtmlTable)
.setInMarkdownTable(prev.inMarkdownTable)
.setTheRest(prev)
.setInList(true);
}

Expand All @@ -144,11 +146,18 @@ private static ParseStateInfo indentList(ParseStateInfo prev, String value) {
return new ParseStateInfo().setInList(true);
}
return new ParseState.ParseStateInfo(prev.src, prev.page, value)
.setInFootnotes(prev.inFootnotes)
.setInHtmlTable(prev.inHtmlTable)
.setInMarkdownTable(prev.inMarkdownTable)
.setTheRest(prev)
.setInList(true);
}

private static ParseStateInfo pushFeatureType(ParseStateInfo prev) {
if (prev == null) {
return new ParseStateInfo().setInFeatureType(0);
}
return new ParseState.ParseStateInfo(prev.src, prev.page, prev.listIndent)
.setTheRest(prev)
.setInFeatureType(prev.inFeatureType + 1);
}
}

private final Deque<ParseState.ParseStateInfo> stack = new ArrayDeque<>();
Expand Down Expand Up @@ -221,6 +230,11 @@ public boolean indentList(String value) {
return true;
}

public boolean pushFeatureType() {
stack.addFirst(ParseStateInfo.pushFeatureType(stack.peek()));
return true;
}

public void pop(boolean pushed) {
if (pushed) {
String source = sourcePageString();
Expand Down Expand Up @@ -262,6 +276,11 @@ public boolean inTable() {
return current != null && (current.inHtmlTable || current.inMarkdownTable);
}

public int featureTypeDepth() {
ParseState.ParseStateInfo current = stack.peek();
return current == null ? 0 : current.inFeatureType;
}

public String sourcePageString() {
ParseState.ParseStateInfo current = stack.peek();
if (current == null || current.page == 0) {
Expand Down
40 changes: 28 additions & 12 deletions src/main/java/dev/ebullient/convert/tools/dnd5e/Json2QuteClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -649,24 +649,40 @@ public String getName() {
return cfSources.getName();
}

void appendText(JsonSource converter, List<String> text, String pageSource) {
void appendLink(JsonSource converter, List<String> text, String pageSource) {
converter.maybeAddBlankLine(text);
text.add("### " + converter.decoratedFeatureTypeName(cfSources, cfNode) + " (Level " + level + ")");
if (!cfSources.primarySource().equalsIgnoreCase(pageSource)) {
text.add(converter.getLabeledSource(cfSources));
String x = converter.decoratedFeatureTypeName(cfSources, cfNode);
text.add(String.format("[%s](#%s)", x, converter.toAnchorTag(x + " (Level " + level + ")")));
}

void appendText(JsonSource converter, List<String> text, String pageSource) {
boolean pushed = converter.parseState().pushFeatureType();
try {
converter.maybeAddBlankLine(text);
text.add("### " + converter.decoratedFeatureTypeName(cfSources, cfNode) + " (Level " + level + ")");
if (!cfSources.primarySource().equalsIgnoreCase(pageSource)) {
text.add(converter.getLabeledSource(cfSources));
}
text.add("");
converter.appendToText(text, cfNode.get("entries"), "####");
} finally {
converter.parseState().pop(pushed);
}
text.add("");
converter.appendToText(text, cfNode.get("entries"), "####");
}

public void appendListItemText(JsonSource converter, List<String> text, String pageSource) {
text.add("**" + converter.decoratedFeatureTypeName(cfSources, cfNode) + "**");
if (!cfSources.primarySource().equalsIgnoreCase(pageSource)) {
text.add(converter.getLabeledSource(cfSources));
boolean pushed = converter.parseState().pushFeatureType();
try {
text.add("**" + converter.decoratedFeatureTypeName(cfSources, cfNode) + "**");
if (!cfSources.primarySource().equalsIgnoreCase(pageSource)) {
text.add(converter.getLabeledSource(cfSources));
}
text.add("");
converter.appendToText(text, cfNode.get("entries"), null);
text.add("");
} finally {
converter.parseState().pop(pushed);
}
text.add("");
converter.appendToText(text, cfNode.get("entries"), null);
text.add("");
}
}

Expand Down
11 changes: 8 additions & 3 deletions src/main/java/dev/ebullient/convert/tools/dnd5e/JsonSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ default int intOrDefault(JsonNode source, String key, int value) {
default int intOrThrow(JsonNode source, String key) {
JsonNode result = source.get(key);
if (result == null || !result.canConvertToInt()) {
throw new IllegalStateException(
"Missing required field, or field is not a number. Key: " + key + "; value: " + result);
tui().errorf("Missing required field, or field is not a number. Key: %s; value: %s; from %s: %s",
key, result, getSources(), source);
return -999;
}
return result.asInt();
}
Expand Down Expand Up @@ -273,7 +274,11 @@ default void appendClassFeatureRef(List<String> text, JsonNode entry, Tools5eInd
if (cf == null) {
return; // skipped or not found
}
if (parseState().inList()) {
if (parseState().featureTypeDepth() > 2) {
tui().errorf("Cycle in class or subclass features found in %s", cf.cfSources);
// this is within an existing feature description. Emit as a link
cf.appendLink(this, text, parseState().getSource(featureType));
} else if (parseState().inList()) {
// emit within an existing list item
cf.appendListItemText(this, text, parseState().getSource(featureType));
} else {
Expand Down

0 comments on commit 3522b08

Please sign in to comment.