public ChainAdapter(
      AnnotationLayer aLayer,
      long aLayerId,
      String aTypeName,
      String aLabelFeatureName,
      String aFirstFeatureName,
      String aNextFeatureName,
      Collection<AnnotationFeature> aFeatures) {
    layer = aLayer;
    layerId = aLayerId;
    annotationTypeName = aTypeName;
    chainFirstFeatureName = aFirstFeatureName;
    linkNextFeatureName = aNextFeatureName;

    features = new LinkedHashMap<String, AnnotationFeature>();
    for (AnnotationFeature f : aFeatures) {
      features.put(f.getName(), f);
    }
  }
  @Override
  protected String getDocumentData() {
    if (!dirty) {
      return docData;
    }

    dirty = false;

    // Clear the rendered document
    docData = EMPTY_DOC;

    // Check if a document is set
    if (getModelObject() == null) {
      return docData;
    }

    // Get CAS from the repository
    JCas jCas = null;
    try {
      jCas = repository.readAnnotationCas(getModelObject());
    } catch (IOException | DataRetrievalFailureException e) {
      LOG.error("Unable to read annotation document", e);
      error("Unable to read annotation document: " + ExceptionUtils.getRootCauseMessage(e));
    }
    // Generate BRAT object model from CAS
    GetDocumentResponse response = new GetDocumentResponse();
    response.setText(jCas.getDocumentText());

    BratAnnotatorModel bratAnnotatorModel = new BratAnnotatorModel();
    SpanAdapter.renderTokenAndSentence(jCas, response, bratAnnotatorModel);

    Map<String[], Queue<String>> colorQueues = new HashMap<>();
    for (AnnotationLayer layer : bratAnnotatorModel.getAnnotationLayers()) {
      if (layer.getName().equals(Token.class.getName())) {
        continue;
      }
      List<AnnotationFeature> features = annotationService.listAnnotationFeature(layer);
      List<AnnotationFeature> invisibleFeatures = new ArrayList<AnnotationFeature>();
      for (AnnotationFeature feature : features) {
        if (!feature.isVisible()) {
          invisibleFeatures.add(feature);
        }
      }
      features.removeAll(invisibleFeatures);

      ColoringStrategy coloringStrategy =
          ColoringStrategy.getBestStrategy(
              annotationService, layer, bratAnnotatorModel.getPreferences(), colorQueues);

      getAdapter(annotationService, layer)
          .render(jCas, features, response, bratAnnotatorModel, coloringStrategy);
    }

    // Serialize BRAT object model to JSON
    try {
      StringWriter out = new StringWriter();
      JsonGenerator jsonGenerator =
          JSONUtil.getJsonConverter().getObjectMapper().getFactory().createGenerator(out);
      jsonGenerator.writeObject(response);
      docData = out.toString();
    } catch (IOException e) {
      error(ExceptionUtils.getRootCauseMessage(e));
    }

    return docData;
  }
  /**
   * Add annotations from the CAS, which is controlled by the window size, to the brat response
   * {@link GetDocumentResponse}
   *
   * @param aJcas The JCAS object containing annotations
   * @param aResponse A brat response containing annotations in brat protocol
   * @param aBratAnnotatorModel Data model for brat annotations
   * @param aColoringStrategy the coloring strategy to render this layer (ignored)
   */
  @Override
  public void render(
      JCas aJcas,
      List<AnnotationFeature> aFeatures,
      GetDocumentResponse aResponse,
      BratAnnotatorModel aBratAnnotatorModel,
      ColoringStrategy aColoringStrategy) {
    // Get begin and end offsets of window content
    int windowBegin =
        BratAjaxCasUtil.selectByAddr(
                aJcas, Sentence.class, aBratAnnotatorModel.getSentenceAddress())
            .getBegin();
    int windowEnd =
        BratAjaxCasUtil.selectByAddr(
                aJcas,
                Sentence.class,
                BratAjaxCasUtil.getLastSentenceAddressInDisplayWindow(
                    aJcas,
                    aBratAnnotatorModel.getSentenceAddress(),
                    aBratAnnotatorModel.getPreferences().getWindowSize()))
            .getEnd();

    // Find the features for the arc and span labels - it is possible that we do not find a
    // feature for arc/span labels because they may have been disabled.
    AnnotationFeature spanLabelFeature = null;
    AnnotationFeature arcLabelFeature = null;
    for (AnnotationFeature f : aFeatures) {
      if (WebAnnoConst.COREFERENCE_TYPE_FEATURE.equals(f.getName())) {
        spanLabelFeature = f;
      }
      if (WebAnnoConst.COREFERENCE_RELATION_FEATURE.equals(f.getName())) {
        arcLabelFeature = f;
      }
    }
    // At this point arc and span feature labels must have been found! If not, the later code
    // will crash.

    Type chainType = getAnnotationType(aJcas.getCas());
    Feature chainFirst = chainType.getFeatureByBaseName(chainFirstFeatureName);

    int colorIndex = 0;
    // Iterate over the chains
    for (FeatureStructure chainFs : selectFS(aJcas.getCas(), chainType)) {
      AnnotationFS linkFs = (AnnotationFS) chainFs.getFeatureValue(chainFirst);
      AnnotationFS prevLinkFs = null;

      // Every chain is supposed to have a different color
      String color =
          ColoringStrategy.PALETTE_NORMAL_FILTERED[
              colorIndex % ColoringStrategy.PALETTE_NORMAL_FILTERED.length];
      // The color index is updated even for chains that have no visible links in the current
      // window because we would like the chain color to be independent of visibility. In
      // particular the color of a chain should not change when switching pages/scrolling.
      colorIndex++;

      // Iterate over the links of the chain
      while (linkFs != null) {
        Feature linkNext = linkFs.getType().getFeatureByBaseName(linkNextFeatureName);
        AnnotationFS nextLinkFs = (AnnotationFS) linkFs.getFeatureValue(linkNext);

        // Is link after window? If yes, we can skip the rest of the chain
        if (linkFs.getBegin() >= windowEnd) {
          break; // Go to next chain
        }

        // Is link before window? We only need links that being within the window and that
        // end within the window
        if (!(linkFs.getBegin() >= windowBegin) && (linkFs.getEnd() <= windowEnd)) {
          // prevLinkFs remains null until we enter the window
          linkFs = nextLinkFs;
          continue; // Go to next link
        }

        String bratTypeName = TypeUtil.getBratTypeName(this);

        // Render span
        {
          String bratLabelText =
              TypeUtil.getBratLabelText(
                  this,
                  linkFs,
                  (spanLabelFeature != null) ? asList(spanLabelFeature) : Collections.EMPTY_LIST);
          Offsets offsets =
              new Offsets(linkFs.getBegin() - windowBegin, linkFs.getEnd() - windowBegin);

          aResponse.addEntity(
              new Entity(
                  BratAjaxCasUtil.getAddr(linkFs), bratTypeName, offsets, bratLabelText, color));
        }

        // Render arc (we do this on prevLinkFs because then we easily know that the current
        // and last link are within the window ;)
        if (prevLinkFs != null) {
          String bratLabelText = null;

          if (linkedListBehavior && arcLabelFeature != null) {
            // Render arc label
            bratLabelText = TypeUtil.getBratLabelText(this, prevLinkFs, asList(arcLabelFeature));
          } else {
            // Render only chain type
            bratLabelText = TypeUtil.getBratLabelText(this, prevLinkFs, Collections.EMPTY_LIST);
          }

          List<Argument> argumentList =
              asList(
                  new Argument("Arg1", BratAjaxCasUtil.getAddr(prevLinkFs)),
                  new Argument("Arg2", BratAjaxCasUtil.getAddr(linkFs)));

          aResponse.addRelation(
              new Relation(
                  BratAjaxCasUtil.getAddr(prevLinkFs),
                  bratTypeName,
                  argumentList,
                  bratLabelText,
                  color));
        }

        //                if (BratAjaxCasUtil.isSame(linkFs, nextLinkFs)) {
        //                    log.error("Loop in CAS detected, aborting rendering of chains");
        //                    break;
        //                }

        prevLinkFs = linkFs;
        linkFs = nextLinkFs;
      }
    }
  }