RecordStructureManager { public Optional<Structure> repeatRecord( Optional<Structure> preceding, RepeatSearch repeatSearch, boolean isFirst) { Optional<List<Structure>> repeat = getRepeat(preceding, repeatSearch); if (repeat.isPresent()) { List<Structure> structures = repeat.get(); if (!isFirst) { recordMatch(structures); } return Optional.of(structures.get(structures.size() - 1)); } else { if (isFirst) { recordMissing(repeatSearch.getCoveredRepeat()); } return Optional.empty(); } } RecordStructureManager(ItemHierarchy<Structure> structureHierarchy); Optional<Structure> select(String path); Optional<Structure> select(SelectorPath selectorPath); RepeatSearch createRepeatSearch(TemplateRecordConfiguration recordDefinition); Optional<Structure> repeatRecord(
      Optional<Structure> preceding, RepeatSearch repeatSearch, boolean isFirst); Optional<Structure> repeatField(
      Optional<Structure> preceding, SelectorPath path, int end); void recordMatch(Structure record); void recordMissing(SelectorPath path);  }
@Test public void testCanRepeatFromStartOfDocument() throws InvalidParameterException { SelectorPath maximal = SelectorPath.parse("Document > Heading"); RepeatSearch repeatUnit = new RepeatSearch(ImmutableList.of(maximal), maximal); Optional<Structure> repeat1 = recordStructureManager.repeatRecord(Optional.empty(), repeatUnit, true); assertTrue(repeat1.isPresent()); assertEquals(heading1, repeat1.get()); Optional<Structure> repeat2 = recordStructureManager.repeatRecord(repeat1, repeatUnit, false); assertFalse(repeat2.isPresent()); }