diff --git a/src/main/java/de/ozgcloud/formcycle/formdata/FormStructureParser.java b/src/main/java/de/ozgcloud/formcycle/formdata/FormStructureParser.java index afe13d3bfe99db1b8aec527bcf6c166802a170fd..d490d348b63297732673f58956244a594103f48d 100644 --- a/src/main/java/de/ozgcloud/formcycle/formdata/FormStructureParser.java +++ b/src/main/java/de/ozgcloud/formcycle/formdata/FormStructureParser.java @@ -11,50 +11,64 @@ import java.util.Objects; import java.util.Optional; import de.xima.fc.form.common.items.XItem; +import de.xima.fc.form.common.models.XPropertyValue; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor class FormStructureParser { + static final String CONTAINER_NAME_ANTRAGSTELLER = "fsBKAllDaten"; + private final Map<String, XItem> formularElementsSorted; private final Deque<FormNode> containerNodes = new ArrayDeque<>(); - private final List<FormNode> nodes = new ArrayList<>(); + private final List<FormNode> resultNodes = new ArrayList<>(); public List<FormNode> parse() { - nodes.clear(); + resultNodes.clear(); for (Map.Entry<String, XItem> entry : formularElementsSorted.entrySet()) { var xItem = entry.getValue(); var itemClass = ItemClass.fromString(xItem.getClassName()); if (isInputNode(itemClass)) { - addItem(xItem); + handleInputNode(xItem); } else if (isNestedContainer(itemClass)) { - var formNode = buildNode(xItem); - getNodes(xItem.getParentId()).add(formNode); - containerNodes.push(formNode); + handleContainerNode(xItem); } } - return nodes; + return resultNodes; + } + + void handleInputNode(XItem xItem) { + getResultNodes(xItem).add(buildNode(xItem)); } - void addItem(XItem xItem) { - getNodes(xItem.getParentId()).add(buildNode(xItem)); + void handleContainerNode(XItem xItem) { + var formNode = buildNode(xItem); + getResultNodes(xItem).add(formNode); + containerNodes.push(formNode); } FormNode buildNode(XItem xItem) { var formNode = FormNode.builder().itemId(xItem.getId()).name(xItem.getName()); - ItemClass.fromString(xItem.getClassName()).getTitleProperty().map(xItem::get).ifPresent(propertyValue -> formNode.title(propertyValue.getString())); + getTitle(xItem).ifPresent(formNode::title); return formNode.build(); } - List<FormNode> getNodes(String parentId) { - return containerNodes.isEmpty() - ? nodes - : getNextContainerNode(parentId).map(FormNode::getNestedElements).orElse(nodes); + Optional<String> getTitle(XItem xItem) { + return ItemClass.fromString(xItem.getClassName()).getTitleProperty().map(xItem::get).map(XPropertyValue::getString); + } + + List<FormNode> getResultNodes(XItem xItem) { + if (CONTAINER_NAME_ANTRAGSTELLER.equalsIgnoreCase(xItem.getName())) { + return resultNodes; + } + return getNextContainerNode(xItem.getParentId()).map(FormNode::getNestedElements).orElse(resultNodes); } Optional<FormNode> getNextContainerNode(String parentId) { var nextContainerNode = containerNodes.peekFirst(); - Objects.requireNonNull(nextContainerNode); + if (Objects.isNull(nextContainerNode)) { + return Optional.empty(); + } while (nonParent(nextContainerNode, parentId)) { containerNodes.pollFirst(); if (containerNodes.isEmpty()) { diff --git a/src/test/java/de/ozgcloud/formcycle/formdata/FormStructureParserITCase.java b/src/test/java/de/ozgcloud/formcycle/formdata/FormStructureParserITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..8a70b4b50bcecddffb7ca85a6a6f49d941a60d6d --- /dev/null +++ b/src/test/java/de/ozgcloud/formcycle/formdata/FormStructureParserITCase.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.formcycle.formdata; + +import static de.ozgcloud.formcycle.formdata.ItemClass.*; +import static de.ozgcloud.formcycle.formdata.StructureMockFactory.*; +import static org.assertj.core.api.Assertions.*; + +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import de.xima.fc.form.common.items.XItem; + +class FormStructureParserITCase { + + private static final String FS_CONTAINER_ID = "xi-fs-n"; + private static final String NEXT_ITEM_ID = "new-item"; + + private static final FormNode EXPECTED_FIELD_SET_NODE = FormNodeTestFactory.createBuilder().values(null).itemId(FS_CONTAINER_ID).build(); + private static final FormNode EXPECTED_TEXT_FIELD_NODE = FormNodeTestFactory.createBuilder().values(null).itemId(NEXT_ITEM_ID).build(); + + @Test + void shouldParseInputItem() { + var expected = FormNodeTestFactory.createBuilder().values(null).build(); + + var formNodes = new FormStructureParser(createItem(TEXT_FIELD)).parse(); + + assertThat(formNodes).hasSize(1).first().usingRecursiveComparison().isEqualTo(expected); + } + + @Test + void shouldParseContainerItem() { + var expected = FormNodeTestFactory.createBuilder().values(null).build(); + + var formNodes = new FormStructureParser(createItem(FIELD_SET)).parse(); + + assertThat(formNodes).hasSize(1).first().usingRecursiveComparison().isEqualTo(expected); + } + + @Test + void shouldNotParseSpanItem() { + var formNodes = new FormStructureParser(createItem(SPAN)).parse(); + + assertThat(formNodes).isEmpty(); + } + + @Test + void shouldParseNestedFieldSetContainer() { + var formNodes = new FormStructureParser(initNestedMap()).parse(); + + assertThat(formNodes).hasSize(2); + var containerNodes = formNodes.get(0).getNestedElements(); + assertThat(containerNodes).hasSize(2); + assertThat(containerNodes.get(0)).usingRecursiveComparison().isEqualTo(EXPECTED_FIELD_SET_NODE); + assertThat(containerNodes.get(1)).usingRecursiveComparison().isEqualTo(EXPECTED_TEXT_FIELD_NODE); + assertThat(formNodes.get(1).getItemId()).isEqualTo(ITEM_ID); + } + + private Map<String, XItem> initNestedMap() { + var containerWithItem = createPageItem(); + containerWithItem.put(CONTAINER_ITEM_ID, getMock(CONTAINER, PAGE_ITEM_ID)); + containerWithItem.put(FS_CONTAINER_ID, getMock(FIELD_SET, FS_CONTAINER_ID, CONTAINER_ITEM_ID, ITEM_TITLE, ITEM_NAME)); + containerWithItem.put(NEXT_ITEM_ID, getMock(TEXT_FIELD, NEXT_ITEM_ID, CONTAINER_ITEM_ID, ITEM_TITLE, ITEM_NAME)); + containerWithItem.put(ITEM_ID, getMock(CHECKBOX, PAGE_ITEM_ID)); + + return containerWithItem; + } +} diff --git a/src/test/java/de/ozgcloud/formcycle/formdata/FormStructureParserTest.java b/src/test/java/de/ozgcloud/formcycle/formdata/FormStructureParserTest.java index 3e4ba50584db125130aec4757839e300303a612f..5eef45b500c8e7d3e193a0eba90230b5de570961 100644 --- a/src/test/java/de/ozgcloud/formcycle/formdata/FormStructureParserTest.java +++ b/src/test/java/de/ozgcloud/formcycle/formdata/FormStructureParserTest.java @@ -3,11 +3,15 @@ package de.ozgcloud.formcycle.formdata; import static de.ozgcloud.formcycle.formdata.ItemClass.*; import static de.ozgcloud.formcycle.formdata.StructureMockFactory.*; import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; import java.util.Deque; import java.util.List; import java.util.Map; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -20,103 +24,186 @@ import lombok.SneakyThrows; class FormStructureParserTest { - private static final String NEXT_ITEM_ID = "new-item"; + private FormStructureParser structureParser; @Nested - class TestItems { + class TestParse { @Test - void shouldParseInputItem() { - var expected = FormNodeTestFactory.createBuilder().values(null).build(); - var structureParser = createParser(TEXT_FIELD); + void shouldClearResultNodes() { + structureParser = new FormStructureParser(Collections.emptyMap()); + injectResultNodes(new ArrayList<>(List.of(FormNodeTestFactory.create()))); - var formNodes = structureParser.parse(); + var result = structureParser.parse(); - assertThat(formNodes).hasSize(1).first().usingRecursiveComparison().isEqualTo(expected); + assertThat(result).isEmpty(); } @Test - void shouldParseContainerItem() { - var expected = FormNodeTestFactory.createBuilder().values(null).build(); - var structureParser = createParser(FIELD_SET); + void shouldReturnResultNodes() { + structureParser = new FormStructureParser(Collections.emptyMap()); + var expectedResultNode = new ArrayList<>(List.of(FormNodeTestFactory.create())); + injectResultNodes(expectedResultNode); - var formNodes = structureParser.parse(); + var result = structureParser.parse(); - assertThat(formNodes).hasSize(1).first().usingRecursiveComparison().isEqualTo(expected); + assertThat(result).isSameAs(expectedResultNode); } - @Test - void shouldNotParseTextItem() { - var structureParser = createParser(SPAN); + @Nested + class TestHandleInputNode { + + @Test + void shouldCallHandleInputNode() { + var items = createItem(TEXT_FIELD); + structureParser = spy(createParser(items)); + + structureParser.parse(); + + verify(structureParser).handleInputNode(items.get(ITEM_ID)); + } + + @Test + void shouldNotCallHandleContainerNode() { + var items = createItem(TEXT_FIELD); + structureParser = spy(createParser(items)); - var formNodes = structureParser.parse(); + structureParser.parse(); + + verify(structureParser, never()).handleContainerNode(any()); + } - assertThat(formNodes).isEmpty(); } - private FormStructureParser createParser(ItemClass itemClass) { - return new FormStructureParser(createItem(itemClass)); + @Nested + class TestHandleContainerNode { + + @Test + void shouldHandleContainerNode() { + var items = createItem(FIELD_SET); + structureParser = spy(createParser(items)); + + structureParser.parse(); + + verify(structureParser).handleContainerNode(items.get(ITEM_ID)); + } + + @Test + void shouldNotCallHandleInputNode() { + var items = createItem(FIELD_SET); + structureParser = spy(createParser(items)); + + structureParser.parse(); + + verify(structureParser, never()).handleInputNode(any()); + } + } + + private FormStructureParser createParser(Map<String, XItem> items) { + return new FormStructureParser(items); + } + } @Nested - class TestNestedItems { + class TestHandleInputNode { - private static final String FS_CONTAINER_ID = "xi-fs-n"; - private static final String NESTED_ITEM_ID1 = "nested1"; - private static final String NESTED_ITEM_ID2 = "nested2"; + @BeforeEach + void init() { + structureParser = spy(new FormStructureParser(createItem(FIELD_SET))); + } - private FormStructureParser structureParser; + @Test + void shouldCallGetResultNodes() { + var xItem = getMock(TEXT_FIELD); + + structureParser.handleInputNode(xItem); + + verify(structureParser).getResultNodes(xItem); + } + + @Test + void shouldCallBuildNode() { + var xItem = getMock(TEXT_FIELD); + doReturn(new ArrayList<>()).when(structureParser).getResultNodes(any()); + + structureParser.handleInputNode(xItem); + + verify(structureParser).buildNode(xItem); + } + + @Test + void shouldAddNodeToResultNodes() { + var resultNodes = new ArrayList<FormNode>(); + doReturn(resultNodes).when(structureParser).getResultNodes(any()); + var formNode = FormNodeTestFactory.create(); + doReturn(formNode).when(structureParser).buildNode(any()); + + structureParser.handleInputNode(getMock(TEXT_FIELD)); + + assertThat(resultNodes).containsExactly(formNode); + } + } + + @Nested + class TestHandleContainerNode { @BeforeEach - void setup() { - var containerWithItem = initNestedMap(); - structureParser = new FormStructureParser(containerWithItem); + void init() { + structureParser = spy(new FormStructureParser(createItem(FIELD_SET))); } @Test - void shouldParseAllInputItems() { - var structure = new FormStructureParser(StructureMockFactory.create()); + void shouldCallBuildNode() { + var xItem = getMock(FIELD_SET); - var formNodes = structure.parse(); + structureParser.handleContainerNode(xItem); - assertThat(formNodes).hasSize(2); + verify(structureParser).buildNode(xItem); } @Test - void shouldParseNestedFieldSetContainer() { - var expectedFsNode = FormNodeTestFactory.createBuilder().values(null).itemId(FS_CONTAINER_ID).build(); - var expectedTfNode = FormNodeTestFactory.createBuilder().values(null).itemId(NEXT_ITEM_ID).build(); + void shouldCallGetResultNodes() { + var xItem = getMock(FIELD_SET); - var formNodes = structureParser.parse(); + structureParser.handleContainerNode(xItem); - assertThat(formNodes).hasSize(2); - var containerNodes = formNodes.get(0).getNestedElements(); - assertThat(containerNodes).hasSize(2).first().usingRecursiveComparison().ignoringFields("nestedElements").isEqualTo(expectedFsNode); - assertThat(containerNodes.get(1)).usingRecursiveComparison().ignoringFields("nestedElements").isEqualTo(expectedTfNode); + verify(structureParser).getResultNodes(xItem); } @Test - void shouldParseItemsInFieldSet() { - var expectedTfNode = FormNodeTestFactory.createBuilder().values(null).itemId(NESTED_ITEM_ID1).build(); + void shouldAddNodeToResultNodes() { + var resultNodes = new ArrayList<FormNode>(); + doReturn(resultNodes).when(structureParser).getResultNodes(any()); + var formNode = FormNodeTestFactory.create(); + doReturn(formNode).when(structureParser).buildNode(any()); - var fieldSetNodes = structureParser.parse().get(0).getNestedElements().get(0).getNestedElements(); + structureParser.handleContainerNode(getMock(FIELD_SET)); - assertThat(fieldSetNodes).hasSize(1).first().usingRecursiveComparison().ignoringFields("nestedElements").isEqualTo(expectedTfNode); + assertThat(resultNodes).containsExactly(formNode); } - private Map<String, XItem> initNestedMap() { - var containerWithItem = createPageItem(); - containerWithItem.put(CONTAINER_ITEM_ID, getMock(CONTAINER, PAGE_ITEM_ID)); - containerWithItem.put(FS_CONTAINER_ID, getMock(FIELD_SET, FS_CONTAINER_ID, CONTAINER_ITEM_ID, ITEM_TITLE, ITEM_NAME)); - containerWithItem.put(NESTED_ITEM_ID1, getMock(TEXT_FIELD, NESTED_ITEM_ID1, FS_CONTAINER_ID, ITEM_TITLE, ITEM_NAME)); - containerWithItem.put(NESTED_ITEM_ID2, getMock(SPAN, NESTED_ITEM_ID2, FS_CONTAINER_ID, ITEM_TITLE, ITEM_NAME)); - containerWithItem.put(NEXT_ITEM_ID, getMock(TEXT_FIELD, NEXT_ITEM_ID, CONTAINER_ITEM_ID, ITEM_TITLE, ITEM_NAME)); - containerWithItem.put(ITEM_ID, getMock(CHECKBOX, PAGE_ITEM_ID)); + @Test + void shouldAddNodeToTopOfContainerNodes() { + var formNode = FormNodeTestFactory.create(); + doReturn(formNode).when(structureParser).buildNode(any()); + var parentNode = injectContainerNodes(); + var fieldSetMock = getMock(FIELD_SET); + when(fieldSetMock.getParentId()).thenReturn(parentNode.getFirst().getItemId()); - return containerWithItem; + structureParser.handleContainerNode(fieldSetMock); + + assertThat(getContainerNodes()).first().isSameAs(formNode); } + @SuppressWarnings("unchecked") + @SneakyThrows + private Deque<FormNode> getContainerNodes() { + var containerNodesField = FormStructureParser.class.getDeclaredField("containerNodes"); + containerNodesField.setAccessible(true); + return (Deque<FormNode>) containerNodesField.get(structureParser); + } } @Nested @@ -178,131 +265,130 @@ class FormStructureParserTest { } @Nested - class TestMethods { - - private static final String PARENT_ID = "parentId"; - - @InjectMocks - private FormStructureParser structureParser; + class TestGetResultNodes { - private Deque<FormNode> containerNodes; - private FormNode containerNode; + private final List<FormNode> expectedResultNodes = List.of(FormNodeTestFactory.create()); - @SneakyThrows @BeforeEach - void setup() { - containerNode = FormNodeTestFactory.create(); - containerNodes = new ArrayDeque<>(); - containerNodes.push(containerNode); - var stackField = structureParser.getClass().getDeclaredField("containerNodes"); - stackField.setAccessible(true); - stackField.set(structureParser, containerNodes); + void init() { + structureParser = spy(new FormStructureParser(createItem(FIELD_SET))); + injectResultNodes(expectedResultNodes); } - @Nested - @DisplayName("GetNextContainerNode") - class TestNextContainerNode { + @Test + void shouldReturnRootNodesWhenNoContainerNodes() { + var nodes = structureParser.getResultNodes(StructureMockFactory.getMock(TEXT_FIELD)); - @Test - @DisplayName("should return empty when container stack is empty") - void shouldReturnEmpty() { - injectNodesField(); + assertThat(nodes).isSameAs(expectedResultNodes); + } - var nextContainerNode = structureParser.getNextContainerNode(PARENT_ID); + @Test + void shouldReturnRootWhenAntragsteller() { + var antragstellerXItem = getMock(CONTAINER); + when(antragstellerXItem.getName()).thenReturn(FormStructureParser.CONTAINER_NAME_ANTRAGSTELLER); + injectContainerNodes(new ArrayDeque<>(List.of(FormNodeTestFactory.createBuilder().itemId(CONTAINER_ITEM_ID).build()))); - assertThat(nextContainerNode).isEmpty(); - } + var nodes = structureParser.getResultNodes(antragstellerXItem); - @Test - void shouldReturnParent() { - var nextContainerNode = structureParser.getNextContainerNode(ITEM_ID); + assertThat(nodes).isSameAs(expectedResultNodes); + } + + @Nested + class TestGetNextContainer { - assertThat(nextContainerNode).isPresent().get().isEqualTo(containerNode); + @BeforeEach + void init() { + injectContainerNodes(); } @Test - void shouldReturnNestedParent() { - var parentContainer = FormNodeTestFactory.createBuilder().itemId(NEXT_ITEM_ID).build(); - containerNodes.addLast(parentContainer); + void shouldReturnRootWhenNoParentNode() { + var nodes = structureParser.getResultNodes(getMock(CONTAINER)); - var nextContainerNode = structureParser.getNextContainerNode(NEXT_ITEM_ID); - - assertThat(nextContainerNode).isPresent().get().isEqualTo(parentContainer); + assertThat(nodes).isSameAs(expectedResultNodes); } @Test - @DisplayName("should return empty when no parent on stack") - void shouldReturnEmptyNoParents() { - var parentContainer = FormNodeTestFactory.createBuilder().itemId(NEXT_ITEM_ID).build(); - containerNodes.addLast(parentContainer); + void shouldCallGetNextContainerNode() { + var parentId = "parentId"; + var xItem = getMock(CONTAINER); + when(xItem.getParentId()).thenReturn(parentId); - var nextContainerNode = structureParser.getNextContainerNode("id"); + structureParser.getResultNodes(xItem); - assertThat(nextContainerNode).isEmpty(); + verify(structureParser).getNextContainerNode(parentId); } @Test - @DisplayName("should clear stack when no parent found") - void shouldClearStack() { - structureParser.getNextContainerNode("id"); + void shouldReturnNestedNodes() { + var formNode = FormNodeTestFactory.create(); + doReturn(Optional.of(formNode)).when(structureParser).getNextContainerNode(anyString()); + var expectedNestedCollection = formNode.getNestedElements(); - assertThat(getStackCollection()).isEmpty(); - } + var nodes = structureParser.getResultNodes(getMock(CONTAINER)); + assertThat(nodes).isSameAs(expectedNestedCollection); + } } + } - @Nested - @DisplayName("GetNodes") - class TestGetNodes { + @Nested + class TestGetNextContainerNode { - @Test - @DisplayName("should return result collection when container stack is empty") - void shouldReturnResultCollectionStackEmpty() { - getStackCollection().clear(); - var expectedNodes = injectNodesField(); + @BeforeEach + void init() { + structureParser = spy(new FormStructureParser(createItem(FIELD_SET))); + } - var resultNodes = structureParser.getNodes(ITEM_ID); + @Test + void shouldReturnEmptyWhenNodeStackEmpty() { + var nextContainerNode = structureParser.getNextContainerNode(ITEM_ID); - assertThat(resultNodes).isEqualTo(expectedNodes); - } + assertThat(nextContainerNode).isEmpty(); + } - @Test - @DisplayName("should return result collection when no parent found") - void shouldReturnResultCollectionNoParent() { - var expectedNodes = injectNodesField(); + @Test + void shouldReturnEmpty() { + injectContainerNodes(new ArrayDeque<>(List.of(FormNodeTestFactory.create()))); - var resultNodes = structureParser.getNodes(PARENT_ID); + var nextContainerNode = structureParser.getNextContainerNode("parentId"); - assertThat(resultNodes).isEqualTo(expectedNodes); - } + assertThat(nextContainerNode).isEmpty(); + } - @Test - @DisplayName("should return container collection when parent found") - void shouldReturnResultContainerCollection() { - var expectedCollection = containerNode.getNestedElements(); + @Test + void shouldReturnParent() { + var parentId = "parentId"; + FormNode parentNode = FormNodeTestFactory.createBuilder().itemId(parentId).build(); + var parentNodes = injectContainerNodes(new ArrayDeque<>()); + parentNodes.push(parentNode); + parentNodes.push(FormNodeTestFactory.create()); - var resultNodes = structureParser.getNodes(ITEM_ID); + var nextContainerNode = structureParser.getNextContainerNode(parentId); - assertThat(resultNodes).isEqualTo(expectedCollection); - } + assertThat(nextContainerNode).contains(parentNode); } + } - @SneakyThrows - private List<FormNode> injectNodesField() { - List<FormNode> nodes = List.of(FormNodeTestFactory.create()); - var nodesField = structureParser.getClass().getDeclaredField("nodes"); - nodesField.setAccessible(true); - nodesField.set(structureParser, nodes); - - return nodes; - } + @SneakyThrows + private List<FormNode> injectResultNodes(List<FormNode> nodes) { + var containerNodesField = FormStructureParser.class.getDeclaredField("resultNodes"); + containerNodesField.setAccessible(true); + containerNodesField.set(structureParser, nodes); + return nodes; + } - @SneakyThrows - private Deque<FormNode> getStackCollection() { - var stackField = structureParser.getClass().getDeclaredField("containerNodes"); - stackField.setAccessible(true); - return (Deque<FormNode>) stackField.get(structureParser); - } + @SneakyThrows + private Deque<FormNode> injectContainerNodes() { + return injectContainerNodes(new ArrayDeque<>(List.of(FormNodeTestFactory.create()))); + } + @SneakyThrows + private Deque<FormNode> injectContainerNodes(Deque<FormNode> containerNodes) { + var containerNodesField = FormStructureParser.class.getDeclaredField("containerNodes"); + containerNodesField.setAccessible(true); + containerNodesField.set(structureParser, containerNodes); + return containerNodes; } + } \ No newline at end of file