docs: public I_CmsExtractionResult extractContent(CmsObject cms, CmsResource resource, I_CmsSearchIndex index)
      throws CmsException {
  
          logContentExtraction(resource, index);
          try {
              CmsFile file = readFile(cms, resource);
              CmsXmlContainerPage containerPage = CmsXmlContainerPageFactory.unmarshal(cms, file);
              Locale locale = index.getLocaleForResource(cms, resource, null);
  
              // initialize return values
              StringBuffer content = new StringBuffer();
              LinkedHashMap<String, String> items = new LinkedHashMap<String, String>();
  
              CmsContainerPageBean containerBean = containerPage.getContainerPage(cms);
              for (CmsContainerElementBean element : containerBean.getElements()) {
                  // check all elements in this container
  
                  // get the formatter configuration for this element
                  element.initResource(cms);
                  CmsADEConfigData adeConfig = OpenCms.getADEManager().lookupConfiguration(cms, file.getRootPath());
                  CmsFormatterConfiguration formatters = adeConfig.getFormatters(cms, element.getResource());
  
                  if (formatters.isSearchContent(element.getFormatterId())) {
                      // the content of this element must be included for the container page
  
                      element.initResource(cms);
                      CmsFile elementFile = readFile(cms, element.getResource());
                      A_CmsXmlDocument elementContent = CmsXmlContentFactory.unmarshal(cms, elementFile);
                      List<String> elementNames = elementContent.getNames(locale);
                      for (String xpath : elementNames) {
                          // xpath will have the form "Text[1]" or "Nested[1]/Text[1]"
                          I_CmsXmlContentValue value = elementContent.getValue(xpath, locale);
                          if (value.getContentDefinition().getContentHandler().isSearchable(value)) {
                              // the content value is searchable
                              String extracted = value.getPlainText(cms);
                              if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(extracted)) {
                                  items.put(elementFile.getRootPath() + "/" + xpath, extracted);
                                  content.append(extracted);
                                  content.append('\n');
                              }
                          }
                      }
                  }
              }
  
              return new CmsExtractionResult(content.toString(), items);
  
          } catch (Exception e) {
              throw new CmsIndexException(
                  Messages.get().container(Messages.ERR_TEXT_EXTRACTION_1, resource.getRootPath()),
                  e);
          }
      }