protected String makeFragment(
     StringBuilder buffer,
     int[] index,
     Field[] values,
     WeightedFragInfo fragInfo,
     String[] preTags,
     String[] postTags,
     Encoder encoder) {
   StringBuilder fragment = new StringBuilder();
   final int s = fragInfo.getStartOffset();
   int[] modifiedStartOffset = {s};
   String src =
       getFragmentSourceMSO(
           buffer, index, values, s, fragInfo.getEndOffset(), modifiedStartOffset);
   int srcIndex = 0;
   for (SubInfo subInfo : fragInfo.getSubInfos()) {
     for (Toffs to : subInfo.getTermsOffsets()) {
       fragment
           .append(
               encoder.encodeText(
                   src.substring(srcIndex, to.getStartOffset() - modifiedStartOffset[0])))
           .append(getPreTag(preTags, subInfo.getSeqnum()))
           .append(
               encoder.encodeText(
                   src.substring(
                       to.getStartOffset() - modifiedStartOffset[0],
                       to.getEndOffset() - modifiedStartOffset[0])))
           .append(getPostTag(postTags, subInfo.getSeqnum()));
       srcIndex = to.getEndOffset() - modifiedStartOffset[0];
     }
   }
   fragment.append(encoder.encodeText(src.substring(srcIndex)));
   return fragment.toString();
 }
  protected List<WeightedFragInfo> discreteMultiValueHighlighting(
      List<WeightedFragInfo> fragInfos, Field[] fields) {
    Map<String, List<WeightedFragInfo>> fieldNameToFragInfos =
        new HashMap<String, List<WeightedFragInfo>>();
    for (Field field : fields) {
      fieldNameToFragInfos.put(field.name(), new ArrayList<WeightedFragInfo>());
    }

    fragInfos:
    for (WeightedFragInfo fragInfo : fragInfos) {
      int fieldStart;
      int fieldEnd = 0;
      for (Field field : fields) {
        if (field.stringValue().isEmpty()) {
          fieldEnd++;
          continue;
        }
        fieldStart = fieldEnd;
        fieldEnd += field.stringValue().length() + 1; // + 1 for going to next field with same name.

        if (fragInfo.getStartOffset() >= fieldStart
            && fragInfo.getEndOffset() >= fieldStart
            && fragInfo.getStartOffset() <= fieldEnd
            && fragInfo.getEndOffset() <= fieldEnd) {
          fieldNameToFragInfos.get(field.name()).add(fragInfo);
          continue fragInfos;
        }

        if (fragInfo.getSubInfos().isEmpty()) {
          continue fragInfos;
        }

        Toffs firstToffs = fragInfo.getSubInfos().get(0).getTermsOffsets().get(0);
        if (fragInfo.getStartOffset() >= fieldEnd || firstToffs.getStartOffset() >= fieldEnd) {
          continue;
        }

        int fragStart = fieldStart;
        if (fragInfo.getStartOffset() > fieldStart && fragInfo.getStartOffset() < fieldEnd) {
          fragStart = fragInfo.getStartOffset();
        }

        int fragEnd = fieldEnd;
        if (fragInfo.getEndOffset() > fieldStart && fragInfo.getEndOffset() < fieldEnd) {
          fragEnd = fragInfo.getEndOffset();
        }

        List<SubInfo> subInfos = new ArrayList<SubInfo>();
        WeightedFragInfo weightedFragInfo =
            new WeightedFragInfo(fragStart, fragEnd, subInfos, fragInfo.getTotalBoost());

        Iterator<SubInfo> subInfoIterator = fragInfo.getSubInfos().iterator();
        while (subInfoIterator.hasNext()) {
          SubInfo subInfo = subInfoIterator.next();
          List<Toffs> toffsList = new ArrayList<Toffs>();
          Iterator<Toffs> toffsIterator = subInfo.getTermsOffsets().iterator();
          while (toffsIterator.hasNext()) {
            Toffs toffs = toffsIterator.next();
            if (toffs.getStartOffset() >= fieldStart && toffs.getEndOffset() <= fieldEnd) {
              toffsList.add(toffs);
              toffsIterator.remove();
            }
          }
          if (!toffsList.isEmpty()) {
            subInfos.add(new SubInfo(subInfo.getText(), toffsList, subInfo.getSeqnum()));
          }

          if (subInfo.getTermsOffsets().isEmpty()) {
            subInfoIterator.remove();
          }
        }
        fieldNameToFragInfos.get(field.name()).add(weightedFragInfo);
      }
    }

    List<WeightedFragInfo> result = new ArrayList<WeightedFragInfo>();
    for (List<WeightedFragInfo> weightedFragInfos : fieldNameToFragInfos.values()) {
      result.addAll(weightedFragInfos);
    }
    Collections.sort(
        result,
        new Comparator<WeightedFragInfo>() {

          public int compare(
              FieldFragList.WeightedFragInfo info1, FieldFragList.WeightedFragInfo info2) {
            return info1.getStartOffset() - info2.getStartOffset();
          }
        });

    return result;
  }