docs: private static void collectSpansPositionsAndTokens(
        Map<MtasSpanQuery, SpanWeight> spansQueryWeight, IndexSearcher searcher,
        CodecInfo mtasCodecInfo, LeafReader r, LeafReaderContext lrc,
        String field, Terms t, List<Integer> docSet, List<Integer> docList,
        ComponentField fieldInfo, FieldInfos fieldInfos, Status status)
        throws IOException {
  
      boolean needSpans = false;
      boolean needPositions = false;
      boolean needTokens = false;
  
      // results
      Map<Integer, Integer> positionsData = null;
      Map<Integer, Integer> tokensData = null;
      Set<MtasSpanQuery> spansNumberByPositions = null;
      Map<MtasSpanQuery, Map<Integer, Integer>> spansNumberData = null;
      Map<MtasSpanQuery, Map<Integer, List<Match>>> spansMatchData = null;
      Map<String, SortedMap<String, int[]>> facetData = null;
      Map<String, String> facetDataType = null;
  
      // collect position stats
      if (!fieldInfo.statsPositionList.isEmpty()) {
        needPositions = true;
      }
      // collect token stats
      if (!fieldInfo.statsTokenList.isEmpty()) {
        needTokens = true;
      }
      if (!fieldInfo.termVectorList.isEmpty()) {
        for (ComponentTermVector ctv : fieldInfo.termVectorList) {
          if (!needPositions) {
            needPositions = (ctv.functions == null
                ? ctv.subComponentFunction.parserFunction.needPositions()
                : ctv.functionNeedPositions());
          }
        }
      }
  
      // compute from spans for selected docs
      if (!fieldInfo.spanQueryList.isEmpty()) {
        // check for statsSpans
        spansNumberByPositions = new HashSet<>();
        spansNumberData = new HashMap<>();
        spansMatchData = new HashMap<>();
        facetData = new HashMap<>();
        facetDataType = new HashMap<>();
        // spans
        if (!fieldInfo.statsSpanList.isEmpty()) {
          for (ComponentSpan cs : fieldInfo.statsSpanList) {
            needPositions = (!needPositions) ? cs.parser.needPositions()
                : needPositions;
            needPositions = (!needPositions) ? cs.functionNeedPositions()
                : needPositions;
            needSpans = (!needSpans) ? cs.parser.needArgumentsNumber() > 0
                : needSpans;
            HashSet<Integer> arguments = cs.parser.needArgument();
            arguments.addAll(cs.functionNeedArguments());
            for (int a : arguments) {
              if (cs.queries.length > a) {
                MtasSpanQuery q = cs.queries[a];
                if (!spansNumberData.containsKey(q)) {
                  spansNumberData.put(q, new HashMap<Integer, Integer>());
                }
              }
            }
          }
        }
        // kwic
        if (!fieldInfo.kwicList.isEmpty()) {
          needSpans = true;
          for (ComponentKwic ck : fieldInfo.kwicList) {
            if (!spansMatchData.containsKey(ck.query)) {
              spansMatchData.put(ck.query, new HashMap<Integer, List<Match>>());
            }
          }
        }
        // list
        if (!fieldInfo.listList.isEmpty()) {
          needSpans = true;
          for (ComponentList cl : fieldInfo.listList) {
            if (!spansMatchData.containsKey(cl.spanQuery)) {
              if (cl.number > 0) {
                // only if needed
                if (cl.position < (cl.start + cl.number)) {
                  spansMatchData.put(cl.spanQuery,
                      new HashMap<Integer, List<Match>>());
                } else {
                  spansNumberData.put(cl.spanQuery,
                      new HashMap<Integer, Integer>());
                }
              } else if (!spansNumberData.containsKey(cl.spanQuery)) {
                spansNumberData.put(cl.spanQuery,
                    new HashMap<Integer, Integer>());
              }
            }
          }
        }
        // group
        if (!fieldInfo.groupList.isEmpty()) {
          needSpans = true;
          for (ComponentGroup cg : fieldInfo.groupList) {
            if (!spansMatchData.containsKey(cg.spanQuery)) {
              spansMatchData.put(cg.spanQuery,
                  new HashMap<Integer, List<Match>>());
            }
          }
        }
        // facet
        if (!fieldInfo.facetList.isEmpty()) {
          for (ComponentFacet cf : fieldInfo.facetList) {
            needPositions = !needPositions ? cf.baseParserNeedPositions()
                : needPositions;
            needPositions = !needPositions ? cf.functionNeedPositions()
                : needPositions;
            for (int i = 0; i < cf.baseFields.length; i++) {
              needSpans = !needSpans ? cf.baseParsers[i].needArgumentsNumber() > 0
                  : needSpans;
              HashSet<Integer> arguments = cf.baseParsers[i].needArgument();
              for (int a : arguments) {
                if (cf.spanQueries.length > a) {
                  MtasSpanQuery q = cf.spanQueries[a];
                  if (!spansNumberData.containsKey(q)) {
                    spansNumberData.put(q, new HashMap<Integer, Integer>());
                  }
                }
              }
              for (MtasFunctionParserFunction function : cf.baseFunctionParserFunctions[i]) {
                needSpans = !needSpans ? function.needArgumentsNumber() > 0
                    : needSpans;
                arguments = function.needArgument();
                for (int a : arguments) {
                  if (cf.spanQueries.length > a) {
                    MtasSpanQuery q = cf.spanQueries[a];
                    if (!spansNumberData.containsKey(q)) {
                      spansNumberData.put(q, new HashMap<Integer, Integer>());
                    }
                  }
                }
              }
              if (!facetData.containsKey(cf.baseFields[i])) {
                facetData.put(cf.baseFields[i], new TreeMap<String, int[]>());
                facetDataType.put(cf.baseFields[i], cf.baseFieldTypes[i]);
              }
            }
          }
        }
        // termvector
        if (!fieldInfo.termVectorList.isEmpty()) {
          for (ComponentTermVector ctv : fieldInfo.termVectorList) {
            if ((ctv.subComponentFunction.parserFunction != null
                && ctv.subComponentFunction.parserFunction.needPositions())
                || (ctv.functions != null && ctv.functionNeedPositions())) {
              needPositions = true;
            }
          }
        }
      }
  
      if (needSpans) {
        Map<Integer, Integer> numberData;
        Map<Integer, List<Match>> matchData;
        // collect values for facetFields
        for (Entry<String, SortedMap<String, int[]>> entry : facetData
            .entrySet()) {
          FieldInfo fi = fieldInfos.fieldInfo(entry.getKey());
          if (fi != null) {
            // prefer to use pointvalue
            if (!fi.getDocValuesType().equals(DocValuesType.NONE)) {
              Iterator<Integer> docIterator = docSet.iterator();
              // numeric or sorted
              if (fi.getDocValuesType().equals(DocValuesType.NUMERIC)
                  || fi.getDocValuesType().equals(DocValuesType.SORTED)
                  || fi.getDocValuesType().equals(DocValuesType.SORTED_SET)
                  || fi.getDocValuesType().equals(DocValuesType.SORTED_NUMERIC)
                  || fi.getDocValuesType().equals(DocValuesType.BINARY)) {
                // create map of values to corresponding docIds
                Map<Object, List<Integer>> facetDataSubList = new HashMap<>();
                // numeric
                if (fi.getDocValuesType().equals(DocValuesType.NUMERIC)) {
                  NumericDocValues docValues = r.getContext().reader()
                      .getNumericDocValues(entry.getKey());
                  int docId;
                  while (docIterator.hasNext()) {
                    docId = docIterator.next() - lrc.docBase;
                    if (docValues.advanceExact(docId)) {
                      long value = docValues.longValue();
                      if (!facetDataSubList.containsKey(value)) {
                        List<Integer> facetDataSubListItem = new ArrayList<>();
                        facetDataSubListItem.add(docId + lrc.docBase);
                        facetDataSubList.put(value, facetDataSubListItem);
                      } else {
                        facetDataSubList.get(value).add(docId + lrc.docBase);
                      }
                    }
                  }
                // sorted numeric
                } else if (fi.getDocValuesType().equals(DocValuesType.SORTED_NUMERIC)) {
                  SortedNumericDocValues docValues = r.getContext().reader()
                      .getSortedNumericDocValues(entry.getKey());
                  int docId;
                  while (docIterator.hasNext()) {
                    docId = docIterator.next() - lrc.docBase;
                    if (docValues.advanceExact(docId)) {
                      int n = docValues.docValueCount();
                      for(int i =0; i<n; i++) {
                        long value = docValues.nextValue();
                        if (!facetDataSubList.containsKey(value)) {
                          List<Integer> facetDataSubListItem = new ArrayList<>();
                          facetDataSubListItem.add(docId + lrc.docBase);
                          facetDataSubList.put(value, facetDataSubListItem);
                        } else {
                          facetDataSubList.get(value).add(docId + lrc.docBase);
                        }  
                      }                    
                    }
                  }
               // sorted set
                } else if (fi.getDocValuesType().equals(DocValuesType.SORTED_SET)) {
                  SortedSetDocValues docValues = r.getContext().reader()
                      .getSortedSetDocValues(entry.getKey());
                  int docId;
                  Map<Long, String> dictionary = new HashMap<>();
                  while (docIterator.hasNext()) {
                    docId = docIterator.next() - lrc.docBase;
                    if (docValues.advanceExact(docId)) {
                      long tmpValue;
                      String value;
                      while((tmpValue=docValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) {
                        if(!dictionary.containsKey(tmpValue)) {
                          value = docValues.lookupOrd(tmpValue).utf8ToString();
                          dictionary.put(tmpValue, value);
                        } else {
                          value = dictionary.get(tmpValue);
                        }
                        if (!facetDataSubList.containsKey(value)) {
                          List<Integer> facetDataSubListItem = new ArrayList<>();
                          facetDataSubListItem.add(docId + lrc.docBase);
                          facetDataSubList.put(value, facetDataSubListItem);
                        } else {
                          facetDataSubList.get(value).add(docId + lrc.docBase);
                        }
                      }
                    }
                  }
                // sorted
                } else if (fi.getDocValuesType().equals(DocValuesType.SORTED)) {
                  SortedDocValues docValues = r.getContext().reader()
                      .getSortedDocValues(entry.getKey());
                  int docId;
                  while (docIterator.hasNext()) {
                    docId = docIterator.next() - lrc.docBase;
                    if (docValues.advanceExact(docId)) {
                      String value = docValues.binaryValue().utf8ToString();
                      if (!facetDataSubList.containsKey(value)) {
                        List<Integer> facetDataSubListItem = new ArrayList<>();
                        facetDataSubListItem.add(docId + lrc.docBase);
                        facetDataSubList.put(value, facetDataSubListItem);
                      } else {
                        facetDataSubList.get(value).add(docId + lrc.docBase);
                      }
                    }
                  }
                }
                if (!facetDataSubList.isEmpty()) {
                  SortedMap<String, int[]> facetDataList = entry.getValue();
                  SimpleDateFormat sdf = new SimpleDateFormat(
                      "yyyy-MM-dd'T'HH:mm:ss'Z'");
                  sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
                  for (Entry<Object, List<Integer>> facetEntry : facetDataSubList
                      .entrySet()) {
                    int[] docIdList = facetEntry.getValue().stream()
                        .mapToInt(i -> i).toArray();
                    String termValue;
                    if (facetDataType.get(entry.getKey())
                        .equals(NumberType.DATE.name())) {
                      Date date = new Date((Long) facetEntry.getKey());
                      termValue = sdf.format(date);
                    } else {
                      termValue = facetEntry.getKey().toString();
                    }
                    if (!facetDataList.containsKey(termValue)) {
                      facetDataList.put(termValue, docIdList);
                    } else {
                      int[] oldList = facetDataList.get(termValue);
                      int[] newList = new int[oldList.length + docIdList.length];
                      System.arraycopy(oldList, 0, newList, 0, oldList.length);
                      System.arraycopy(docIdList, 0, newList, oldList.length,
                          docIdList.length);
                      facetDataList.put(termValue, newList);
                    }
                  }
                }
              } else {
                throw new IOException("facets for docValues of type "
                    + fi.getDocValuesType() + " not implemented");
              }
            } else if (!docSet.isEmpty()) {
              if (facetDataType.get(entry.getKey())
                  .equals(ComponentFacet.TYPE_POINTFIELD_WITHOUT_DOCVALUES)) {
                throw new IOException(
                    "can't create facets for pointField without docValues");
              }
              Terms fft = r.terms(entry.getKey());
              if (fft != null) {
                TermsEnum termsEnum = fft.iterator();
                BytesRef term = null;
                PostingsEnum postingsEnum = null;
                SortedMap<String, int[]> facetDataList = entry.getValue();
                while ((term = termsEnum.next()) != null) {
                  int docId;
                  int termDocId = -1;
                  int[] facetDataSublist = new int[docSet.size()];
                  int facetDataSublistCounter = 0;
                  Iterator<Integer> docIterator = docSet.iterator();
                  postingsEnum = termsEnum.postings(postingsEnum);
                  while (docIterator.hasNext()) {
                    docId = docIterator.next() - lrc.docBase;
                    if (docId >= termDocId
                        && ((docId == termDocId) || ((termDocId = postingsEnum
                            .advance(docId)) == docId))) {
                      facetDataSublist[facetDataSublistCounter] = docId
                          + lrc.docBase;
                      facetDataSublistCounter++;
                    }
                  }
                  if (facetDataSublistCounter > 0) {
                    String termValue = null;
                    if (facetDataType.get(entry.getKey())
                        .equals(NumberType.INTEGER.name())) {
                      // only values without shifting bits
                      if (term.bytes[term.offset] == LegacyNumericUtils.SHIFT_START_INT) {
                        termValue = Integer
                            .toString(LegacyNumericUtils.prefixCodedToInt(term));
                      } else {
                        continue;
                      }
                    } else if (facetDataType.get(entry.getKey())
                        .equals(NumberType.LONG.name())) {
                      if (term.bytes[term.offset] == LegacyNumericUtils.SHIFT_START_LONG) {
                        termValue = Long
                            .toString(LegacyNumericUtils.prefixCodedToLong(term));
                      } else {
                        continue;
                      }
                    } else {
                      termValue = term.utf8ToString();
                    }
                    if (!facetDataList.containsKey(termValue)) {
                      facetDataList.put(termValue, Arrays.copyOf(facetDataSublist,
                          facetDataSublistCounter));
                    } else {
                      int[] oldList = facetDataList.get(termValue);
                      int[] newList = new int[oldList.length
                          + facetDataSublistCounter];
                      System.arraycopy(oldList, 0, newList, 0, oldList.length);
                      System.arraycopy(facetDataSublist, 0, newList,
                          oldList.length, facetDataSublistCounter);
                      facetDataList.put(termValue, newList);
                    }
                  }
                }
              }
            }
          }
        }
  
        // collect matches and numbers for queries
        for (MtasSpanQuery sq : fieldInfo.spanQueryList) {
          // what to collect : numbers
          if (spansNumberData.containsKey(sq)) {
            numberData = spansNumberData.get(sq);
          } else {
            numberData = null;
          }
          // what to collect: matches
          if (spansMatchData.containsKey(sq)) {
            matchData = spansMatchData.get(sq);
          } else {
            matchData = null;
          }
          boolean doNormalCollection = true;
          // if only number is needed, possibly termvectors can be used
          if ((numberData != null) && (matchData == null)) {
            if (sq.isMatchAllPositionsQuery()) {
              spansNumberByPositions.add(sq);
              needPositions = true;
              doNormalCollection = false;
            }
          }
          // collect (if termvector collection didn't work)
          if (doNormalCollection
              && ((numberData != null) || (matchData != null))) {
            Spans spans = spansQueryWeight.get(sq).getSpans(lrc,
                SpanWeight.Postings.POSITIONS);
            if (spans != null) {
              Iterator<Integer> it;
              if (docSet != null) {
                it = docSet.iterator();
              } else {
                it = docList.iterator();
              }
              if (it.hasNext()) {
                int docId = it.next();
                int number;
                ArrayList<Match> matchDataList;
                Integer spansDocId = null;
                while (docId != DocIdSetIterator.NO_MORE_DOCS) {
                  if (spans.advance(
                      (docId - lrc.docBase)) == DocIdSetIterator.NO_MORE_DOCS) {
                    break;
                  }
                  spansDocId = spans.docID() + lrc.docBase;
                  while ((docId < spansDocId) && it.hasNext()) {
                    docId = it.next();
                  }
                  if (docId < spansDocId) {
                    break;
                  }
                  if (spansDocId.equals(docId)) {
                    number = 0;
                    matchDataList = new ArrayList<>();
                    int tmpStartPosition;
                    while ((tmpStartPosition = spans
                        .nextStartPosition()) != Spans.NO_MORE_POSITIONS) {
                      number++;
                      if (matchData != null) {
                        Match m = new Match(tmpStartPosition,
                            spans.endPosition());
                        matchDataList.add(m);
                      }
                    }
                    if ((numberData != null)) {
                      numberData.put(spansDocId, number);
                    }
                    if ((matchData != null)) {
                      matchData.put(spansDocId, matchDataList);
                    }
                    if (it.hasNext()) {
                      docId = it.next();
                    } else {
                      break;
                    }
                  }
                }
              }
            }
          }
        }
      }
  
      // collect position stats
      if (needPositions) {
        if (mtasCodecInfo != null) {
          // for relatively small numbers, compute only what is needed
          if (docSet.size() < Math.log(r.maxDoc())) {
            positionsData = new HashMap<>();
            for (int docId : docSet) {
              positionsData.put(docId, mtasCodecInfo.getNumberOfPositions(field,
                  (docId - lrc.docBase)));
            }
            // compute everything, only use what is needed
          } else {
            positionsData = mtasCodecInfo.getAllNumberOfPositions(field,
                lrc.docBase);
            for (int docId : docSet) {
              if (!positionsData.containsKey(docId)) {
                positionsData.put(docId, 0);
              }
            }
          }
        } else {
          positionsData = new HashMap<>();
          for (int docId : docSet) {
            positionsData.put(docId, 0);
          }
        }
        if (spansNumberByPositions != null && spansNumberData != null) {        
          for (MtasSpanQuery sq : spansNumberByPositions) {
            Map<Integer,Integer> numberData = spansNumberData.get(sq);
            positionsData.forEach((k,v) -> numberData.put(k,v!=null?v:0));          
          }
        }
      }
  
      // collect token stats
      if (needTokens) {
        if (mtasCodecInfo != null) {
          // for relatively small numbers, compute only what is needed
          if (docSet.size() < Math.log(r.maxDoc())) {
            tokensData = new HashMap<>();
            for (int docId : docSet) {
              tokensData.put(docId,
                  mtasCodecInfo.getNumberOfTokens(field, (docId - lrc.docBase)));
            }
            // compute everything, only use what is needed
          } else {
            tokensData = mtasCodecInfo.getAllNumberOfTokens(field, lrc.docBase);
            for (int docId : docSet) {
              if (!tokensData.containsKey(docId)) {
                tokensData.put(docId, 0);
              }
            }
          }
        } else {
          tokensData = new HashMap<>();
          for (int docId : docSet) {
            tokensData.put(docId, 0);
          }
        }
      }
  
      if (!fieldInfo.statsPositionList.isEmpty()) {
        // create positions
        createPositions(fieldInfo.statsPositionList, positionsData, docSet);
      }
  
      if (!fieldInfo.statsTokenList.isEmpty()) {
        // create positions
        createTokens(fieldInfo.statsTokenList, tokensData, docSet);
      }
  
      if (!fieldInfo.documentList.isEmpty()) {
        // create document
        createDocument(fieldInfo.documentList, docList, fieldInfo.uniqueKeyField,
            searcher, t, lrc);
      }
      if (!fieldInfo.spanQueryList.isEmpty()) {
        if (!fieldInfo.statsSpanList.isEmpty()) {
          // create stats
          createStats(fieldInfo.statsSpanList, positionsData, spansNumberData,
              docSet.toArray(new Integer[docSet.size()]));
        }
        if (!fieldInfo.listList.isEmpty()) {
          // create list
          createList(fieldInfo.listList, spansNumberData, spansMatchData, docSet,
              field, lrc.docBase, fieldInfo.uniqueKeyField, mtasCodecInfo,
              searcher);
        }
        if (!fieldInfo.groupList.isEmpty()) {
          // create group
          createGroup(fieldInfo.groupList, spansMatchData, docSet,
              fieldInfos.fieldInfo(field), field, lrc.docBase, mtasCodecInfo,
              searcher, lrc, status);
        }
        if (!fieldInfo.kwicList.isEmpty()) {
          // create kwic
          createKwic(fieldInfo.kwicList, spansMatchData, docList, field,
              lrc.docBase, fieldInfo.uniqueKeyField, mtasCodecInfo, searcher);
        }
        if (!fieldInfo.facetList.isEmpty()) {
          // create facets
          createFacet(fieldInfo.facetList, positionsData, spansNumberData,
              facetData, docSet);
        }
      }
      if (!fieldInfo.termVectorList.isEmpty()) {
        createTermvectorFull(fieldInfo.termVectorList, positionsData, docSet, t,
            r, lrc);
        createTermvectorFirstRound(fieldInfo.termVectorList, positionsData,
            docSet, t, r, lrc);
      }
    }