docs: protected void computeMappingsFromObject(
        MtasTokenIdFactory mtasTokenIdFactory, MtasParserObject object,
        Map<String, List<MtasParserObject>> currentList,
        Map<String, Map<Integer, Set<String>>> updateList)
        throws MtasParserException, MtasConfigException {
      MtasParserType<MtasParserMapping<?>> objectType = object.getType();
      List<MtasParserMapping<?>> mappings = objectType.getItems();
      if (!object.updateableMappingsWithPosition.isEmpty()) {
        for (int tokenId : object.updateableMappingsWithPosition) {
          updateList.get(UPDATE_TYPE_POSITION).put(tokenId, object.getRefIds());
        }
      }
      if (!object.updateableMappingsWithOffset.isEmpty()) {
        for (int tokenId : object.updateableMappingsWithOffset) {
          updateList.get(UPDATE_TYPE_OFFSET).put(tokenId, object.getRefIds());
        }
      }
      for (MtasParserMapping<?> mapping : mappings) {
        try {
          if (mapping.getTokens().isEmpty()) {
            // empty exception
          } else {
            for (int i = 0; i < mapping.getTokens().size(); i++) {
              MtasParserMappingToken mappingToken = mapping.getTokens().get(i);
              // empty exception
              if (mappingToken.preValues.isEmpty()) {
                // continue, but no token
              } else {
                // check conditions
                postcheckMappingConditions(object, mapping.getConditions(),
                    currentList);
                boolean containsVariables = checkForVariables(
                    mappingToken.preValues);
                containsVariables = !containsVariables
                    ? checkForVariables(mappingToken.postValues)
                    : containsVariables;
                // construct preValue
                String[] preValue = computeValueFromMappingValues(object,
                    mappingToken.preValues, currentList, containsVariables);
                // at least preValue
                if (preValue == null || preValue.length == 0) {
                  throw new MtasParserException("no preValues");
                } else {
                  // no delimiter in preValue
                  for (int k = 0; k < preValue.length; k++) {
                    if ((preValue[k] = preValue[k].replace(MtasToken.DELIMITER,
                        "")).isEmpty()) {
                      throw new MtasParserException("empty preValue");
                    }
                  }
                }
                // construct postValue
                String[] postValue = computeValueFromMappingValues(object,
                    mappingToken.postValues, currentList, containsVariables);
                // construct value
                String[] value;
                if (postValue == null || postValue.length == 0) {
                  value = preValue.clone();
                  for (int k = 0; k < value.length; k++) {
                    value[k] = value[k] + MtasToken.DELIMITER;
                  }
                } else if (postValue.length == 1) {
                  value = preValue.clone();
                  for (int k = 0; k < value.length; k++) {
                    value[k] = value[k] + MtasToken.DELIMITER + postValue[0];
                  }
                } else if (preValue.length == 1) {
                  value = postValue.clone();
                  for (int k = 0; k < value.length; k++) {
                    value[k] = preValue[0] + MtasToken.DELIMITER + value[k];
                  }
                } else {
                  value = new String[preValue.length * postValue.length];
                  int number = 0;
                  for (int k1 = 0; k1 < preValue.length; k1++) {
                    for (int k2 = 0; k2 < postValue.length; k2++) {
                      value[number] = preValue[k1] + MtasToken.DELIMITER
                          + postValue[k2];
                      number++;
                    }
                  }
                }
                // construct payload
                BytesRef payload = computePayloadFromMappingPayload(object,
                    mappingToken.payload, currentList);
                // create token and get id: from now on, we must continue, no
                // exceptions allowed...
                for (int k = 0; k < value.length; k++) {
                  MtasTokenString token = new MtasTokenString(
                      mtasTokenIdFactory.createTokenId(), value[k]);
                  // store settings offset, realoffset and parent
                  token.setProvideOffset(mappingToken.offset);
                  token.setProvideRealOffset(mappingToken.realoffset);
                  token.setProvideParentId(mappingToken.parent);
                  String checkType = object.objectType.getType();
                  // register token if it contains variables
                  if (containsVariables) {
                    updateList.get(UPDATE_TYPE_VARIABLE).put(token.getId(), null);
                  }
                  // register id for update when parent is created
                  if (!currentList.get(checkType).isEmpty()) {
                    if (currentList.get(checkType).contains(object)) {
                      int listPosition = currentList.get(checkType)
                          .indexOf(object);
                      if (listPosition > 0) {
                        currentList.get(checkType).get(listPosition - 1)
                            .registerUpdateableMappingAtParent(token.getId());
                      }
                    } else {
                      currentList.get(checkType)
                          .get(currentList.get(checkType).size() - 1)
                          .registerUpdateableMappingAtParent(token.getId());
                    }
                    // if no real ancestor, register id update when group
                    // ancestor is created
                  } else if (!currentList.get(MAPPING_TYPE_GROUP).isEmpty()) {
                    currentList.get(MAPPING_TYPE_GROUP)
                        .get(currentList.get(MAPPING_TYPE_GROUP).size() - 1)
                        .registerUpdateableMappingAtParent(token.getId());
                  } else if (!currentList.get(MAPPING_TYPE_RELATION).isEmpty()) {
                    currentList.get(MAPPING_TYPE_RELATION)
                        .get(currentList.get(MAPPING_TYPE_RELATION).size() - 1)
                        .registerUpdateableMappingAtParent(token.getId());
                  }
                  // update children
                  for (Integer tmpId : object.getUpdateableMappingsAsParent()) {
                    if (tokenCollection.get(tmpId) != null) {
                      tokenCollection.get(tmpId).setParentId(token.getId());
                    }
                  }
                  object.resetUpdateableMappingsAsParent();
                  // use own position
                  if (mapping.position.equals(MtasParserMapping.SOURCE_OWN)) {
                    token.addPositions(object.getPositions());
                    // use position from ancestorGroup
                  } else if (mapping.position
                      .equals(MtasParserMapping.SOURCE_ANCESTOR_GROUP)
                      && (!currentList.get(MAPPING_TYPE_GROUP).isEmpty())) {
                    currentList.get(MAPPING_TYPE_GROUP)
                        .get(currentList.get(MAPPING_TYPE_GROUP).size() - 1)
                        .addUpdateableMappingWithPosition(token.getId());
                    // use position from ancestorWord
                  } else if (mapping.position
                      .equals(MtasParserMapping.SOURCE_ANCESTOR_WORD)
                      && (!currentList.get(MAPPING_TYPE_WORD).isEmpty())) {
                    currentList.get(MAPPING_TYPE_WORD)
                        .get(currentList.get(MAPPING_TYPE_WORD).size() - 1)
                        .addUpdateableMappingWithPosition(token.getId());
                    // use position from ancestorRelation
                  } else if (mapping.position
                      .equals(MtasParserMapping.SOURCE_ANCESTOR_RELATION)
                      && (!currentList.get(MAPPING_TYPE_RELATION).isEmpty())) {
                    currentList.get(MAPPING_TYPE_RELATION)
                        .get(currentList.get(MAPPING_TYPE_RELATION).size() - 1)
                        .addUpdateableMappingWithPosition(token.getId());
                    // register id to get positions later from references
                  } else if (mapping.position
                      .equals(MtasParserMapping.SOURCE_REFS)) {
                    if (mapping.type.equals(MAPPING_TYPE_GROUP_ANNOTATION)) {
                      if (mapping.start != null && mapping.end != null) {
                        String start = object.getAttribute(mapping.start);
                        String end = object.getAttribute(mapping.end);
                        if (start != null && !start.isEmpty() && end != null
                            && !end.isEmpty()) {
                          if (start.startsWith("#")) {
                            start = start.substring(1);
                          }
                          if (end.startsWith("#")) {
                            end = end.substring(1);
                          }
                          updateList.get(UPDATE_TYPE_LOCAL_REF_POSITION_START)
                              .put(token.getId(),
                                  new HashSet<String>(Arrays.asList(start)));
                          updateList.get(UPDATE_TYPE_LOCAL_REF_POSITION_END).put(
                              token.getId(),
                              new HashSet<String>(Arrays.asList(end)));
                          updateList.get(UPDATE_TYPE_LOCAL_REF_OFFSET_START).put(
                              token.getId(),
                              new HashSet<String>(Arrays.asList(start)));
                          updateList.get(UPDATE_TYPE_LOCAL_REF_OFFSET_END).put(
                              token.getId(),
                              new HashSet<String>(Arrays.asList(end)));
                        }
                      }
                    } else {
                      updateList.get(UPDATE_TYPE_POSITION).put(token.getId(),
                          object.getRefIds());
                    }
                  } else {
                    // should not happen
                  }
                  // use own offset
                  if (mapping.offset.equals(MtasParserMapping.SOURCE_OWN)) {
                    token.setOffset(object.getOffsetStart(),
                        object.getOffsetEnd());
                    // use offset from ancestorGroup
                  } else if (mapping.offset
                      .equals(MtasParserMapping.SOURCE_ANCESTOR_GROUP)
                      && (!currentList.get(MAPPING_TYPE_GROUP).isEmpty())) {
                    currentList.get(MAPPING_TYPE_GROUP)
                        .get(currentList.get(MAPPING_TYPE_GROUP).size() - 1)
                        .addUpdateableMappingWithOffset(token.getId());
                    // use offset from ancestorWord
                  } else if (mapping.offset
                      .equals(MtasParserMapping.SOURCE_ANCESTOR_WORD)
                      && !currentList.get(MAPPING_TYPE_WORD).isEmpty()) {
                    currentList.get(MAPPING_TYPE_WORD)
                        .get(currentList.get(MAPPING_TYPE_WORD).size() - 1)
                        .addUpdateableMappingWithOffset(token.getId());
                    // use offset from ancestorRelation
                  } else if (mapping.offset
                      .equals(MtasParserMapping.SOURCE_ANCESTOR_RELATION)
                      && !currentList.get(MAPPING_TYPE_RELATION).isEmpty()) {
                    currentList.get(MAPPING_TYPE_RELATION)
                        .get(currentList.get(MAPPING_TYPE_RELATION).size() - 1)
                        .addUpdateableMappingWithOffset(token.getId());
                    // register id to get offset later from refs
                  } else if (mapping.offset
                      .equals(MtasParserMapping.SOURCE_REFS)) {
                    updateList.get(UPDATE_TYPE_OFFSET).put(token.getId(),
                        object.getRefIds());
                  }
                  // always use own realOffset
                  token.setRealOffset(object.getRealOffsetStart(),
                      object.getRealOffsetEnd());
                  // set payload
                  token.setPayload(payload);
                  // add token to collection
                  tokenCollection.add(token);
                }
              }
            }
          }
          // register start and end
          if (mapping.start != null && mapping.end != null) {
            String startAttribute = null;
            String endAttribute = null;
            if (mapping.start.equals("#")) {
              startAttribute = object.getId();
            } else {
              startAttribute = object.getAttribute(mapping.start);
              if (startAttribute != null && startAttribute.startsWith("#")) {
                startAttribute = startAttribute.substring(1);
              }
            }
            if (mapping.end.equals("#")) {
              endAttribute = object.getId();
            } else {
              endAttribute = object.getAttribute(mapping.end);
              if (endAttribute != null && endAttribute.startsWith("#")) {
                endAttribute = endAttribute.substring(1);
              }
            }
            if (startAttribute != null && endAttribute != null
                && !object.getPositions().isEmpty()) {
              object.setReferredStartPosition(startAttribute,
                  object.getPositions().first());
              object.setReferredEndPosition(endAttribute,
                  object.getPositions().last());
              object.setReferredStartOffset(startAttribute,
                  object.getOffsetStart());
              object.setReferredEndOffset(endAttribute, object.getOffsetEnd());
            }
          }
        } catch (MtasParserException e) {
          log.debug("Rejected mapping " + object.getType().getName(), e);
          // ignore, no new token is created
        }
      }
      // copy remaining updateableMappings to new parent
      if (!currentList.get(objectType.getType()).isEmpty()) {
        if (currentList.get(objectType.getType()).contains(object)) {
          int listPosition = currentList.get(objectType.getType())
              .indexOf(object);
          if (listPosition > 0) {
            currentList.get(objectType.getType()).get(listPosition - 1)
                .registerUpdateableMappingsAtParent(
                    object.getUpdateableMappingsAsParent());
          }
        } else {
          currentList.get(objectType.getType())
              .get(currentList.get(objectType.getType()).size() - 1)
              .registerUpdateableMappingsAtParent(
                  object.getUpdateableMappingsAsParent());
        }
      } else if (!currentList.get(MAPPING_TYPE_GROUP).isEmpty()) {
        currentList.get(MAPPING_TYPE_GROUP)
            .get(currentList.get(MAPPING_TYPE_GROUP).size() - 1)
            .registerUpdateableMappingsAtParent(
                object.getUpdateableMappingsAsParent());
      } else if (!currentList.get(MAPPING_TYPE_RELATION).isEmpty()) {
        currentList.get(MAPPING_TYPE_RELATION)
            .get(currentList.get(MAPPING_TYPE_RELATION).size() - 1)
            .registerUpdateableMappingsAtParent(
                object.getUpdateableMappingsAsParent());
      }
      updateMappingsWithLocalReferences(object, currentList, updateList);
    }