code: private static String wordShapeDan2(String s, Collection<String> knownLCWords) {
      StringBuilder sb = new StringBuilder("WT-");
      char lastM = '~';
      boolean nonLetters = false;
      int len = s.length();
      for (int i = 0; i < len; i++) {
        char c = s.charAt(i);
        char m = c;
        if (Character.isDigit(c)) {
          m = 'd';
        } else if (Character.isLowerCase(c) || c == '_') {
          m = 'x';
        } else if (Character.isUpperCase(c)) {
          m = 'X';
        }
        if (m != 'x' && m != 'X') {
          nonLetters = true;
        }
        if (m != lastM) {
          sb.append(m);
        }
        lastM = m;
      }
      if (len <= 3) {
        sb.append(':').append(len);
      }
      if (knownLCWords != null) {
        if (!nonLetters && knownLCWords.contains(s.toLowerCase())) {
          sb.append('k');
        }
      }
      // System.err.println("wordShapeDan2: " + s + " became " + sb);
      return sb.toString();
    }
docs: A fine-grained word shape classifier, that equivalence classes
  lower and upper case and digits, and collapses sequences of the
  same type, but keeps all punctuation, etc. <p>
  <i>Note:</i> We treat '_' as a lowercase letter, sort of like many
  programming languages.  We do this because we use '_' joining of
  tokens in some applications like RTE.
  
  @param s           The String whose shape is to be returned
  @param knownLCWords If this is non-null and non-empty, mark words whose
  lower case form is found in the
  Collection of known lower case words
  @return The word shape