private void appendSource(
     final StringBuilder builder,
     final int start,
     final int end,
     boolean escapeSource,
     boolean sourceEscaped) {
   if (sourceEscaped == escapeSource) {
     append(builder, codePoints.substring(start, end), escapeSource, sourceEscaped);
   } else if (escapeSource) {
     append(builder, escape(codePoints.substring(start, end)), true, sourceEscaped);
   } else {
     append(builder, unescape(codePoints.substring(start, end)), false, sourceEscaped);
   }
 }
 public Pair<String, List<SpanItem>> buildWithIndices() {
   List<SpanItem> items = new ArrayList<>();
   if (links.isEmpty()) return Pair.create(escapeSource(source), items);
   Collections.sort(links);
   final StringBuilder sb = new StringBuilder();
   final int linksSize = links.size();
   for (int i = 0; i < linksSize; i++) {
     final LinkSpec spec = links.get(i);
     if (spec == null) {
       continue;
     }
     final int start = spec.start, end = spec.end;
     if (i == 0) {
       if (start >= 0 && start <= codePointsLength) {
         appendSource(sb, 0, start, false, sourceIsEscaped);
       }
     } else if (i > 0) {
       final int lastEnd = links.get(i - 1).end;
       if (lastEnd >= 0 && lastEnd <= start && start <= codePointsLength) {
         appendSource(sb, lastEnd, start, false, sourceIsEscaped);
       }
     }
     int spanStart = sb.length();
     if (start >= 0 && start <= end && end <= codePointsLength) {
       if (isEmpty(spec.display)) {
         append(sb, spec.link, false, false);
       } else {
         append(sb, spec.display, false, spec.displayIsHtml);
       }
     }
     final SpanItem item = new SpanItem();
     item.start = spanStart;
     item.end = sb.length();
     item.link = spec.link;
     items.add(item);
     if (i == linksSize - 1 && end >= 0 && end <= codePointsLength) {
       appendSource(sb, end, codePointsLength, false, sourceIsEscaped);
     }
   }
   return Pair.create(sb.toString(), items);
 }
 public String build() {
   if (links.isEmpty()) return escapeSource(source);
   Collections.sort(links);
   final StringBuilder sb = new StringBuilder();
   final int linksSize = links.size();
   for (int i = 0; i < linksSize; i++) {
     final LinkSpec spec = links.get(i);
     if (spec == null) {
       continue;
     }
     final int start = spec.start, end = spec.end;
     if (i == 0) {
       if (start >= 0 && start <= codePointsLength) {
         appendSource(sb, 0, start, shouldReEscape, sourceIsEscaped);
       }
     } else if (i > 0) {
       final int lastEnd = links.get(i - 1).end;
       if (lastEnd >= 0 && lastEnd <= start && start <= codePointsLength) {
         appendSource(sb, lastEnd, start, shouldReEscape, sourceIsEscaped);
       }
     }
     sb.append("<a href=\"");
     sb.append(spec.link);
     sb.append("\">");
     if (start >= 0 && start <= end && end <= codePointsLength) {
       if (isEmpty(spec.display)) {
         append(sb, spec.link, shouldReEscape, false);
       } else {
         append(sb, spec.display, shouldReEscape, spec.displayIsHtml);
       }
     }
     sb.append("</a>");
     if (i == linksSize - 1 && end >= 0 && end <= codePointsLength) {
       appendSource(sb, end, codePointsLength, shouldReEscape, sourceIsEscaped);
     }
   }
   return sb.toString();
 }