RED-6009 - Document Tree Structure

* renamed terminal to leaf
This commit is contained in:
Kilian Schuettler 2023-05-09 14:32:53 +02:00
parent e31a7b1a2f
commit c151b4a94b
16 changed files with 66 additions and 66 deletions

View File

@ -60,8 +60,8 @@ public class DocumentDataMapper {
Long[] atomicTextBlocks;
if (entry.getNode().isTerminal()) {
atomicTextBlocks = toAtomicTextBlockIds(entry.getNode().getTerminalTextBlock());
if (entry.getNode().isLeaf()) {
atomicTextBlocks = toAtomicTextBlockIds(entry.getNode().getLeafTextBlock());
} else {
atomicTextBlocks = new Long[]{};
}

View File

@ -82,9 +82,9 @@ public class DocumentGraphMapper {
default -> throw new UnsupportedOperationException("Not yet implemented for type " + entryData.getType());
};
if (node.isTerminal()) {
if (node.isLeaf()) {
TextBlock textBlock = toTextBlock(entryData.getAtomicBlocks(), context, node);
node.setTerminalTextBlock(textBlock);
node.setLeafTextBlock(textBlock);
}
List<Integer> tocId = Arrays.stream(entryData.getTreeId()).boxed().toList();
node.setTreeId(tocId);
@ -104,7 +104,7 @@ public class DocumentGraphMapper {
private Headline buildHeadline(Context context, boolean terminal) {
return Headline.builder().terminal(terminal).documentTree(context.documentTree()).build();
return Headline.builder().leaf(terminal).documentTree(context.documentTree()).build();
}
@ -128,7 +128,7 @@ public class DocumentGraphMapper {
TableCell.TableCellBuilder builder = TableCell.builder();
PropertiesMapper.parseTableCellProperties(properties, builder);
return builder.terminal(terminal).documentTree(context.documentTree()).build();
return builder.leaf(terminal).documentTree(context.documentTree()).build();
}
@ -142,13 +142,13 @@ public class DocumentGraphMapper {
private Footer buildFooter(Context context, boolean terminal) {
return Footer.builder().terminal(terminal).documentTree(context.documentTree()).build();
return Footer.builder().leaf(terminal).documentTree(context.documentTree()).build();
}
private Header buildHeader(Context context, boolean terminal) {
return Header.builder().terminal(terminal).documentTree(context.documentTree()).build();
return Header.builder().leaf(terminal).documentTree(context.documentTree()).build();
}
@ -162,7 +162,7 @@ public class DocumentGraphMapper {
private Paragraph buildParagraph(Context context, boolean terminal) {
return Paragraph.builder().terminal(terminal).documentTree(context.documentTree()).build();
return Paragraph.builder().leaf(terminal).documentTree(context.documentTree()).build();
}

View File

@ -94,12 +94,12 @@ public class DocumentGraphFactory {
if (node instanceof Headline headline) {
List<Integer> tocId = context.documentTree.createNewChildEntryAndReturnId(parentNode.getTreeId(), NodeType.HEADLINE, node);
headline.setTerminalTextBlock(textBlock);
headline.setLeafTextBlock(textBlock);
headline.setTreeId(tocId);
}
if (node instanceof Paragraph paragraph) {
List<Integer> tocId = context.documentTree.createNewChildEntryAndReturnId(parentNode.getTreeId(), NodeType.PARAGRAPH, node);
paragraph.setTerminalTextBlock(textBlock);
paragraph.setLeafTextBlock(textBlock);
paragraph.setTreeId(tocId);
}
}
@ -166,7 +166,7 @@ public class DocumentGraphFactory {
page);
List<Integer> tocId = context.getDocumentTree().createNewMainEntryAndReturnId(NodeType.FOOTER, footer);
footer.setTreeId(tocId);
footer.setTerminalTextBlock(textBlock);
footer.setLeafTextBlock(textBlock);
page.setFooter(footer);
}
@ -182,7 +182,7 @@ public class DocumentGraphFactory {
page);
List<Integer> tocId = context.getDocumentTree().createNewMainEntryAndReturnId(NodeType.HEADER, header);
header.setTreeId(tocId);
header.setTerminalTextBlock(textBlock);
header.setLeafTextBlock(textBlock);
page.setHeader(header);
}
@ -194,7 +194,7 @@ public class DocumentGraphFactory {
AtomicTextBlock textBlock = context.textBlockFactory.emptyTextBlock(footer, context, page);
List<Integer> tocId = context.getDocumentTree().createNewMainEntryAndReturnId(NodeType.FOOTER, footer);
footer.setTreeId(tocId);
footer.setTerminalTextBlock(textBlock);
footer.setLeafTextBlock(textBlock);
page.setFooter(footer);
}
@ -206,7 +206,7 @@ public class DocumentGraphFactory {
AtomicTextBlock textBlock = context.textBlockFactory.emptyTextBlock(header, 0, page);
List<Integer> tocId = context.getDocumentTree().createNewMainEntryAndReturnId(NodeType.HEADER, header);
header.setTreeId(tocId);
header.setTerminalTextBlock(textBlock);
header.setLeafTextBlock(textBlock);
page.setHeader(header);
}

View File

@ -95,27 +95,27 @@ public class TableNodeFactory {
tableCell.setTreeId(tocId);
if (cell.getTextBlocks().isEmpty()) {
tableCell.setTerminalTextBlock(context.getTextBlockFactory().emptyTextBlock(parentNode, context, page));
tableCell.setTerminal(true);
tableCell.setLeafTextBlock(context.getTextBlockFactory().emptyTextBlock(parentNode, context, page));
tableCell.setLeaf(true);
} else if (cell.getTextBlocks().size() == 1) {
textBlock = context.getTextBlockFactory().buildAtomicTextBlock(cell.getTextBlocks().get(0).getSequences(), tableCell, context, page);
tableCell.setTerminalTextBlock(textBlock);
tableCell.setTerminal(true);
tableCell.setLeafTextBlock(textBlock);
tableCell.setLeaf(true);
} else if (firstTextBlockIsHeadline(cell)) {
SectionNodeFactory.addSection(tableCell, cell.getTextBlocks().stream().map(tb -> (AbstractPageBlock) tb).toList(), emptyList(), context);
tableCell.setTerminal(false);
tableCell.setLeaf(false);
} else if (cellAreaIsSmallerThanPageAreaTimesThreshold(cell, page)) {
List<TextPositionSequence> sequences = TextPositionOperations.mergeAndSortTextPositionSequenceByYThenX(cell.getTextBlocks());
textBlock = context.getTextBlockFactory().buildAtomicTextBlock(sequences, tableCell, context, page);
tableCell.setTerminalTextBlock(textBlock);
tableCell.setTerminal(true);
tableCell.setLeafTextBlock(textBlock);
tableCell.setLeaf(true);
} else {
cell.getTextBlocks().forEach(tb -> DocumentGraphFactory.addParagraphOrHeadline(tableCell, tb, context, emptyList()));
tableCell.setTerminal(false);
tableCell.setLeaf(false);
}
}

View File

@ -36,7 +36,7 @@ public class DocumentTree {
public TextBlock buildTextBlock() {
return streamAllEntriesInOrder().map(Entry::getNode).filter(SemanticNode::isTerminal).map(SemanticNode::getTerminalTextBlock).collect(new TextBlockCollector());
return streamAllEntriesInOrder().map(Entry::getNode).filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock).collect(new TextBlockCollector());
}

View File

@ -52,7 +52,7 @@ public class Document implements SemanticNode {
public Stream<TextBlock> streamTerminalTextBlocksInOrder() {
return streamAllNodes().filter(SemanticNode::isTerminal).map(SemanticNode::getTerminalTextBlock);
return streamAllNodes().filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock);
}

View File

@ -24,10 +24,10 @@ import lombok.experimental.FieldDefaults;
public class Footer implements SemanticNode {
List<Integer> treeId;
TextBlock terminalTextBlock;
TextBlock leafTextBlock;
@Builder.Default
boolean terminal = true;
boolean leaf = true;
@EqualsAndHashCode.Exclude
DocumentTree documentTree;
@ -40,14 +40,14 @@ public class Footer implements SemanticNode {
@Override
public TextBlock buildTextBlock() {
return terminalTextBlock;
return leafTextBlock;
}
@Override
public String toString() {
return treeId + ": " + NodeType.FOOTER + ": " + terminalTextBlock.buildSummary();
return treeId + ": " + NodeType.FOOTER + ": " + leafTextBlock.buildSummary();
}
}

View File

@ -24,10 +24,10 @@ import lombok.experimental.FieldDefaults;
public class Header implements SemanticNode {
List<Integer> treeId;
TextBlock terminalTextBlock;
TextBlock leafTextBlock;
@Builder.Default
boolean terminal = true;
boolean leaf = true;
@EqualsAndHashCode.Exclude
DocumentTree documentTree;
@ -40,14 +40,14 @@ public class Header implements SemanticNode {
@Override
public TextBlock buildTextBlock() {
return terminalTextBlock;
return leafTextBlock;
}
@Override
public String toString() {
return treeId + ": " + NodeType.HEADER + ": " + terminalTextBlock.buildSummary();
return treeId + ": " + NodeType.HEADER + ": " + leafTextBlock.buildSummary();
}
}

View File

@ -24,10 +24,10 @@ import lombok.experimental.FieldDefaults;
public class Headline implements SemanticNode {
List<Integer> treeId;
TextBlock terminalTextBlock;
TextBlock leafTextBlock;
@Builder.Default
boolean terminal = true;
boolean leaf = true;
@EqualsAndHashCode.Exclude
DocumentTree documentTree;
@ -40,14 +40,14 @@ public class Headline implements SemanticNode {
@Override
public TextBlock buildTextBlock() {
return terminalTextBlock;
return leafTextBlock;
}
@Override
public String toString() {
return treeId + ": " + NodeType.HEADLINE + ": " + terminalTextBlock.buildSummary();
return treeId + ": " + NodeType.HEADLINE + ": " + leafTextBlock.buildSummary();
}

View File

@ -58,7 +58,7 @@ public class Image implements SemanticNode {
@Override
public TextBlock buildTextBlock() {
return streamAllSubNodes().filter(SemanticNode::isTerminal).map(SemanticNode::getTerminalTextBlock).collect(new TextBlockCollector());
return streamAllSubNodes().filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock).collect(new TextBlockCollector());
}

View File

@ -46,7 +46,7 @@ public class Page {
public TextBlock getMainBodyTextBlock() {
return mainBody.stream().filter(SemanticNode::isTerminal).map(SemanticNode::getTerminalTextBlock).collect(new TextBlockCollector());
return mainBody.stream().filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock).collect(new TextBlockCollector());
}

View File

@ -22,10 +22,10 @@ import lombok.experimental.FieldDefaults;
public class Paragraph implements SemanticNode {
List<Integer> treeId;
TextBlock terminalTextBlock;
TextBlock leafTextBlock;
@Builder.Default
boolean terminal = true;
boolean leaf = true;
@EqualsAndHashCode.Exclude
DocumentTree documentTree;
@ -38,14 +38,14 @@ public class Paragraph implements SemanticNode {
@Override
public TextBlock buildTextBlock() {
return terminalTextBlock;
return leafTextBlock;
}
@Override
public String toString() {
return treeId + ": " + NodeType.PARAGRAPH + ": " + terminalTextBlock.buildSummary();
return treeId + ": " + NodeType.PARAGRAPH + ": " + leafTextBlock.buildSummary();
}
}

View File

@ -40,7 +40,7 @@ public class Section implements SemanticNode {
public TextBlock buildTextBlock() {
if (textBlock == null) {
textBlock = streamAllSubNodes().filter(SemanticNode::isTerminal).map(SemanticNode::getTerminalTextBlock).collect(new TextBlockCollector());
textBlock = streamAllSubNodes().filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock).collect(new TextBlockCollector());
}
return textBlock;
}

View File

@ -22,7 +22,7 @@ public interface SemanticNode {
/**
* Searches all Nodes located underneath this Node in the DocumentTree and concatenates their AtomicTextBlocks into a single TextBlockEntity.
* So, for a Section all TextBlocks of Subsections, Paragraphs, and Tables are concatenated into a single TextBlockEntity
* If the Node is Terminal, the TerminalTextBlock will be returned instead.
* If the Node is a Leaf, the LeafTextBlock will be returned instead.
*
* @return TextBlock containing all AtomicTextBlocks that are located under this Node.
*/
@ -131,36 +131,36 @@ public interface SemanticNode {
/**
* Terminal means a SemanticNode has direct access to a TextBlock, by default this is false and must be overridden.
* Currently only Sections, Images, and Tables are not terminal.
* A TableCell might be Terminal depending on its area compared to the page.
* Leaf means a SemanticNode has direct access to a TextBlock, by default this is false and must be overridden.
* Currently only Sections, Images, and Tables are not leaves.
* A TableCell might be a leaf depending on its area compared to the page.
*
* @return boolean, indicating if a Node has direct access to a TextBlock
*/
default boolean isTerminal() {
default boolean isLeaf() {
return false;
}
/**
* Terminal means a SemanticNode has direct access to a TextBlock, by default this is false and must be overridden.
* Currently only Sections and Tables are not terminal.
* Leaf means a SemanticNode has direct access to a TextBlock, by default this is false and must be overridden.
* Currently only Sections and Tables are no leaves.
*
* @return AtomicTextBlock
*/
default TextBlock getTerminalTextBlock() {
default TextBlock getLeafTextBlock() {
throw new UnsupportedOperationException("Only terminal Nodes have access to TerminalTextBlocks!");
throw new UnsupportedOperationException("Only leaf Nodes have access to LeafTextBlocks!");
}
/**
* Should only be used during construction of the Graph. Sets the TerminalTextBlock of this SemanticNode.
* Should only be used during construction of the Graph. Sets the LeafTextBlock of this SemanticNode.
*
* @param textBlock the TextBlock to set as the terminalTextBlock of this SemanticNode
* @param textBlock the TextBlock to set as the LeafTextBlock of this SemanticNode
*/
default void setTerminalTextBlock(TextBlock textBlock) {
default void setLeafTextBlock(TextBlock textBlock) {
throw new UnsupportedOperationException();
}
@ -345,7 +345,7 @@ public interface SemanticNode {
/**
* If this Node is Terminal it will calculate the boundingBox of its TerminalTextBlock, otherwise it will calculate the Union of the BoundingBoxes of all its Children.
* If this Node is a Leaf it will calculate the boundingBox of its LeafTextBlock, otherwise it will calculate the Union of the BoundingBoxes of all its Children.
* If called on the Document, it will return the cropbox of each page
*
* @return Rectangle2D fully encapsulating this Node for each page.
@ -353,8 +353,8 @@ public interface SemanticNode {
default Map<Page, Rectangle2D> getBBox() {
Map<Page, Rectangle2D> bBoxPerPage = new HashMap<>();
if (isTerminal()) {
return getBBoxFromTerminalTextBlock(bBoxPerPage);
if (isLeaf()) {
return getBBoxFromLeafTextBlock(bBoxPerPage);
}
return getBBoxFromChildren(bBoxPerPage);
@ -397,7 +397,7 @@ public interface SemanticNode {
* @param bBoxPerPage initial empty BoundingBox
* @return The union of all BoundingBoxes of the TextBlock of this node
*/
private Map<Page, Rectangle2D> getBBoxFromTerminalTextBlock(Map<Page, Rectangle2D> bBoxPerPage) {
private Map<Page, Rectangle2D> getBBoxFromLeafTextBlock(Map<Page, Rectangle2D> bBoxPerPage) {
Map<Page, List<AtomicTextBlock>> atomicTextBlockPerPage = buildTextBlock().getAtomicTextBlocks().stream().collect(Collectors.groupingBy(AtomicTextBlock::getPage));
atomicTextBlockPerPage.forEach((page, atbs) -> bBoxPerPage.put(page, RectangleTransformations.bBoxUnionAtomicTextBlock(atbs)));

View File

@ -174,7 +174,7 @@ public class Table implements SemanticNode {
public TextBlock buildTextBlock() {
if (textBlock == null) {
textBlock = streamAllSubNodes().filter(SemanticNode::isTerminal).map(SemanticNode::getTerminalTextBlock).collect(new TextBlockCollector());
textBlock = streamAllSubNodes().filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock).collect(new TextBlockCollector());
}
return textBlock;
}

View File

@ -34,8 +34,8 @@ public class TableCell implements SemanticNode {
Rectangle2D bBox;
@Builder.Default
boolean terminal = true;
TextBlock terminalTextBlock;
boolean leaf = true;
TextBlock leafTextBlock;
TextBlock textBlock;
@ -59,12 +59,12 @@ public class TableCell implements SemanticNode {
@Override
public TextBlock buildTextBlock() {
if (terminal) {
return terminalTextBlock;
if (leaf) {
return leafTextBlock;
}
if (textBlock == null) {
textBlock = streamAllSubNodes().filter(SemanticNode::isTerminal).map(SemanticNode::getTerminalTextBlock).collect(new TextBlockCollector());
textBlock = streamAllSubNodes().filter(SemanticNode::isLeaf).map(SemanticNode::getLeafTextBlock).collect(new TextBlockCollector());
}
return textBlock;
}