boolean stem(char[] term, int len) {
    
    result = null;
    
    k = len - 1;
    if ((k <= 1) || (k >= MaxWordLen - 1)) {
      return false; // don't stem
    }
    
    // first check the stemmer dictionaries, and avoid using the
    // cache if it's in there.
    DictEntry entry = dict_ht.get(term, 0, len);
    if (entry != null) {
      if (entry.root != null) {
        result = entry.root;
        return true;
      }
      return false;
    }
    
    /***
     * caching off is normally faster if (cache == null) initializeStemHash();
     * 
     * // now check the cache, before we copy chars to "word" if (cache != null)
     * { String val = cache.get(term, 0, len); if (val != null) { if (val !=
     * SAME) { result = val; return true; } return false; } }
     ***/
    
    word.reset();
    // allocate enough space so that an expansion is never needed
    word.reserve(len + 10);
    for (int i = 0; i < len; i++) {
      char ch = term[i];
      if (!isAlpha(ch)) return false; // don't stem
      // don't lowercase... it's a requirement that lowercase filter be
      // used before this stemmer.
      word.unsafeWrite(ch);
    }
    
    matchedEntry = null;
    /***
     * lookups.clear(); lookups.add(word.toString());
     ***/
    
    /*
     * This while loop will never be executed more than one time; it is here
     * only to allow the break statement to be used to escape as soon as a word
     * is recognized
     */
    while (true) {
      // YCS: extra lookup()s were inserted so we don't need to
      // do an extra wordInDict() here.
      plural();
      if (matched()) break;
      pastTense();
      if (matched()) break;
      aspect();
      if (matched()) break;
      ityEndings();
      if (matched()) break;
      nessEndings();
      if (matched()) break;
      ionEndings();
      if (matched()) break;
      erAndOrEndings();
      if (matched()) break;
      lyEndings();
      if (matched()) break;
      alEndings();
      if (matched()) break;
      entry = wordInDict();
      iveEndings();
      if (matched()) break;
      izeEndings();
      if (matched()) break;
      mentEndings();
      if (matched()) break;
      bleEndings();
      if (matched()) break;
      ismEndings();
      if (matched()) break;
      icEndings();
      if (matched()) break;
      ncyEndings();
      if (matched()) break;
      nceEndings();
      matched();
      break;
    }
    
    /*
     * try for a direct mapping (allows for cases like `Italian'->`Italy' and
     * `Italians'->`Italy')
     */
    entry = matchedEntry;
    if (entry != null) {
      result = entry.root; // may be null, which means that "word" is the stem
    }
    
    // no entry matched means result is "word"
    return true;
  }
Stems the text in the token. Returns true if changed.
linguistics/src/main/java/com/yahoo/language/simple/kstem/KStemmer.java