private static void addAnnotationSpan(String input, SpanList parent, Token token, StemMode mode,
                                          boolean lowercase, TermOccurrences termOccurrences, int maxTokenLength) {
        if ( ! token.isSpecialToken()) {
            if (token.getNumComponents() > 0) {
                for (int i = 0; i < token.getNumComponents(); ++i) {
                    addAnnotationSpan(input, parent, token.getComponent(i), mode, lowercase, termOccurrences, maxTokenLength);
                }
                return;
            }
            if ( ! token.isIndexable()) return;
        }
        if (token.getOffset() >= input.length()) {
            throw new IllegalArgumentException(token + " has offset " + token.getOffset() + ", which is outside the " +
                                               "bounds of the input string '" + input + "'");
        }
        if (token.getOffset() + token.getOrig().length() > input.length()) {
            throw new IllegalArgumentException(token + " has offset " + token.getOffset() + ", which makes it overflow " +
                                               "the bounds of the input string; " + input);
        }
        if (mode == StemMode.ALL) {
            Span where = parent.span((int)token.getOffset(), token.getOrig().length());
            String indexableOriginal = lowercase ? toLowerCase(token.getOrig()) : token.getOrig();
            String term = token.getTokenString();
            if (term != null) {
                addAnnotation(where, term, token.getOrig(), termOccurrences, maxTokenLength);
                if ( ! term.equals(indexableOriginal))
                    addAnnotation(where, indexableOriginal, token.getOrig(), termOccurrences, maxTokenLength);
            }
            for (int i = 0; i < token.getNumStems(); i++) {
                String stem = token.getStem(i);
                if (! (stem.equals(indexableOriginal) || stem.equals(term)))
                    addAnnotation(where, stem, token.getOrig(), termOccurrences, maxTokenLength);
            }
        } else {
            String term = token.getTokenString();
            if (term == null || term.trim().isEmpty()) return;
            if (term.length() > maxTokenLength) return;
            if (termOccurrences.termCountBelowLimit(term))
                parent.span((int)token.getOffset(), token.getOrig().length()).annotate(termAnnotation(term, token.getOrig()));
        }
    }
Creates a TERM annotation which has the term as annotation (only) if it is different from the original.
indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/linguistics/LinguisticsAnnotator.java