docs: protected CmsEntity readEntity(
          CmsXmlContent content,
          Element element,
          Locale locale,
          String entityId,
          String parentPath,
          String typeName,
          CmsContentTypeVisitor visitor,
          boolean includeInvisible,
          CmsEntity editedLocalEntity) {
  
          String newEntityId = entityId + (CmsStringUtil.isNotEmptyOrWhitespaceOnly(parentPath) ? "/" + parentPath : "");
          CmsEntity newEntity = new CmsEntity(newEntityId, typeName);
          CmsEntity result = newEntity;
  
          List<Element> elements = element.elements();
          CmsType type = visitor.getTypes().get(typeName);
          boolean isChoice = type.isChoice();
          String choiceTypeName = null;
          // just needed for choice attributes
          Map<String, Integer> attributeCounter = null;
          if (isChoice) {
              choiceTypeName = type.getAttributeTypeName(CmsType.CHOICE_ATTRIBUTE_NAME);
              type = visitor.getTypes().get(type.getAttributeTypeName(CmsType.CHOICE_ATTRIBUTE_NAME));
              attributeCounter = new HashMap<String, Integer>();
          }
          int counter = 0;
          CmsObject cms = getCmsObject();
          String previousName = null;
          if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(parentPath)) {
              parentPath += "/";
          }
          for (Element child : elements) {
              String attributeName = getAttributeName(child.getName(), typeName);
              String subTypeName = type.getAttributeTypeName(attributeName);
              if (visitor.getTypes().get(subTypeName) == null) {
                  // in case there is no type configured for this element, the schema may have changed, skip the element
                  continue;
              }
              if (!includeInvisible && !visitor.getAttributeConfigurations().get(attributeName).isVisible()) {
                  // skip attributes marked as invisible, there content should not be transfered to the client
                  continue;
              }
              if (isChoice && (attributeCounter != null)) {
                  if (!attributeName.equals(previousName)) {
                      if (attributeCounter.get(attributeName) != null) {
                          counter = attributeCounter.get(attributeName).intValue();
                      } else {
                          counter = 0;
                      }
                      previousName = attributeName;
                  }
                  attributeCounter.put(attributeName, Integer.valueOf(counter + 1));
              } else if (!attributeName.equals(previousName)) {
  
                  // reset the attribute counter for every attribute name
                  counter = 0;
  
                  previousName = attributeName;
              }
              if (isChoice) {
                  result = new CmsEntity(
                      newEntityId + "/" + CmsType.CHOICE_ATTRIBUTE_NAME + "_" + child.getName() + "[" + counter + "]",
                      choiceTypeName);
                  newEntity.addAttributeValue(CmsType.CHOICE_ATTRIBUTE_NAME, result);
              }
              String path = parentPath + child.getName();
              if (visitor.isDynamicallyLoaded(attributeName)) {
                  I_CmsXmlContentValue value = content.getValue(path, locale, counter);
                  String attributeValue = getDynamicAttributeValue(
                      content.getFile(),
                      value,
                      attributeName,
                      editedLocalEntity);
                  result.addAttributeValue(attributeName, attributeValue);
              } else if (visitor.getTypes().get(subTypeName).isSimpleType()) {
                  I_CmsXmlContentValue value = content.getValue(path, locale, counter);
                  result.addAttributeValue(attributeName, value.getStringValue(cms));
              } else {
                  CmsEntity subEntity = readEntity(
                      content,
                      child,
                      locale,
                      entityId,
                      path + "[" + (counter + 1) + "]",
                      subTypeName,
                      visitor,
                      includeInvisible,
                      editedLocalEntity);
                  result.addAttributeValue(attributeName, subEntity);
  
              }
              counter++;
          }
          return newEntity;
      }