private static synchronized void initCache() {
      if (CACHE.size() <= 0) {
        for (FormatTokenEnum token : FormatTokenEnum.values()) {

          List<Character> tokenKeys = new ArrayList<Character>();

          if (token.name().contains("_")) {
            String[] tokens = token.name().split("_");
            for (String tokenLets : tokens) {
              tokenKeys.add(tokenLets.toUpperCase().charAt(0));
            }
          } else {
            tokenKeys.add(token.name().toUpperCase().charAt(0));
          }

          for (Character tokenKey : tokenKeys) {
            List<FormatTokenEnum> l = CACHE.get(tokenKey);
            if (l == null) {
              l = new ArrayList<FormatTokenEnum>(1);
              CACHE.put(tokenKey, l);
            }
            l.add(token);
          }
        }
      }
    }
 @Override
 public void parse(ToDateParser params, FormatTokenEnum formatTokenEnum, String formatTokenStr) {
   Calendar result = params.getResultCalendar();
   String s = params.getInputStr();
   String inputFragmentStr = null;
   int dateNr = 0;
   switch (formatTokenEnum) {
     case MONTH:
       inputFragmentStr = setByName(result, params, Calendar.MONTH, Calendar.LONG);
       break;
     case Q /*NOT supported yet*/:
       throwException(params, format("token '%s' not supported yet.", formatTokenEnum.name()));
       break;
     case MON:
       inputFragmentStr = setByName(result, params, Calendar.MONTH, Calendar.SHORT);
       break;
     case MM:
       // Note: In Calendar Month go from 0 - 11
       inputFragmentStr =
           matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr);
       result.set(Calendar.MONTH, dateNr - 1);
       break;
     case RM:
       dateNr = 0;
       for (String monthName : ROMAN_MONTH) {
         dateNr++;
         int len = monthName.length();
         if (s.length() >= len && monthName.equalsIgnoreCase(s.substring(0, len))) {
           result.set(Calendar.MONTH, dateNr);
           inputFragmentStr = monthName;
           break;
         }
       }
       if (inputFragmentStr == null || inputFragmentStr.isEmpty()) {
         throwException(
             params,
             format(
                 "Issue happened when parsing token '%s'. " + "Expected one of: %s",
                 formatTokenEnum.name(), Arrays.toString(ROMAN_MONTH)));
       }
       break;
     default:
       throw new IllegalArgumentException(
           format(
               "%s: Internal Error. Unhandled case: %s",
               this.getClass().getSimpleName(), formatTokenEnum));
   }
   params.remove(inputFragmentStr, formatTokenStr);
 }
 @Override
 public void parse(ToDateParser params, FormatTokenEnum formatTokenEnum, String formatTokenStr) {
   Calendar result = params.getResultCalendar();
   String inputFragmentStr = null;
   int dateNr = 0;
   switch (formatTokenEnum) {
     case SYYYY:
     case YYYY:
     case IYYY:
       inputFragmentStr = matchStringOrThrow(PATTERN_FOUR_DIGITS, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr);
       // Gregorian calendar does not have a year 0.
       // 0 = 0001 BC, -1 = 0002 BC, ... so we adjust
       result.set(Calendar.YEAR, dateNr >= 0 ? dateNr : dateNr + 1);
       break;
     case YYY:
     case IYY:
       inputFragmentStr = matchStringOrThrow(PATTERN_THREE_DIGITS, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr);
       // Gregorian calendar does not have a year 0.
       // 0 = 0001 BC, -1 = 0002 BC, ... so we adjust
       result.set(Calendar.YEAR, dateNr >= 0 ? dateNr : dateNr + 1);
       break;
     case RRRR:
       inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr);
       dateNr += dateNr < 50 ? 2000 : 1900;
       result.set(Calendar.YEAR, dateNr);
       break;
     case RR:
       Calendar calendar = Calendar.getInstance();
       int cc = calendar.get(Calendar.YEAR) / 100;
       inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr) + cc * 100;
       result.set(Calendar.YEAR, dateNr);
       break;
     case EE /*NOT supported yet*/:
       throwException(params, format("token '%s' not supported yet.", formatTokenEnum.name()));
       break;
     case E /*NOT supported yet*/:
       throwException(params, format("token '%s' not supported yet.", formatTokenEnum.name()));
       break;
     case YY:
     case IY:
       inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr);
       // Gregorian calendar does not have a year 0.
       // 0 = 0001 BC, -1 = 0002 BC, ... so we adjust
       result.set(Calendar.YEAR, dateNr >= 0 ? dateNr : dateNr + 1);
       break;
     case SCC:
     case CC:
       inputFragmentStr = matchStringOrThrow(PATTERN_TWO_DIGITS, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr) * 100;
       result.set(Calendar.YEAR, dateNr);
       break;
     case Y:
     case I:
       inputFragmentStr = matchStringOrThrow(PATTERN_ONE_DIGIT, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr);
       // Gregorian calendar does not have a year 0.
       // 0 = 0001 BC, -1 = 0002 BC, ... so we adjust
       result.set(Calendar.YEAR, dateNr >= 0 ? dateNr : dateNr + 1);
       break;
     case BC_AD:
       inputFragmentStr = matchStringOrThrow(PATTERN_BC_AD, params, formatTokenEnum);
       if (inputFragmentStr.toUpperCase().startsWith("B")) {
         result.set(Calendar.ERA, GregorianCalendar.BC);
       } else {
         result.set(Calendar.ERA, GregorianCalendar.AD);
       }
       break;
     default:
       throw new IllegalArgumentException(
           format(
               "%s: Internal Error. Unhandled case: %s",
               this.getClass().getSimpleName(), formatTokenEnum));
   }
   params.remove(inputFragmentStr, formatTokenStr);
 }
 @Override
 public void parse(ToDateParser params, FormatTokenEnum formatTokenEnum, String formatTokenStr) {
   Calendar result = params.getResultCalendar();
   String inputFragmentStr = null;
   int dateNr = 0;
   switch (formatTokenEnum) {
     case HH24:
       inputFragmentStr =
           matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr);
       result.set(Calendar.HOUR_OF_DAY, dateNr);
       break;
     case HH12:
     case HH:
       inputFragmentStr =
           matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr);
       result.set(Calendar.HOUR, dateNr);
       break;
     case MI:
       inputFragmentStr =
           matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr);
       result.set(Calendar.MINUTE, dateNr);
       break;
     case SS:
       inputFragmentStr =
           matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr);
       result.set(Calendar.SECOND, dateNr);
       break;
     case SSSSS:
       inputFragmentStr = matchStringOrThrow(PATTERN_NUMBER, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr);
       result.set(Calendar.HOUR_OF_DAY, 0);
       result.set(Calendar.MINUTE, 0);
       result.set(Calendar.SECOND, dateNr);
       break;
     case FF:
       inputFragmentStr = matchStringOrThrow(PATTERN_NUMBER, params, formatTokenEnum);
       String paddedRightNrStr = format("%-9s", inputFragmentStr).replace(' ', '0');
       paddedRightNrStr = paddedRightNrStr.substring(0, 9);
       Double nineDigits = Double.parseDouble(paddedRightNrStr);
       params.setNanos(nineDigits.intValue());
       dateNr = (int) Math.round(nineDigits / 1000000.0);
       result.set(Calendar.MILLISECOND, dateNr);
       break;
     case AM_PM:
       inputFragmentStr = matchStringOrThrow(PATTERN_AM_PM, params, formatTokenEnum);
       if (inputFragmentStr.toUpperCase().startsWith("A")) {
         result.set(Calendar.AM_PM, Calendar.AM);
       } else {
         result.set(Calendar.AM_PM, Calendar.PM);
       }
       break;
     case TZH:
       inputFragmentStr =
           matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr);
       TimeZone tz = result.getTimeZone();
       int offsetMillis = tz.getRawOffset();
       // purge min and sec
       offsetMillis = (offsetMillis / MILLIS_IN_HOUR) * MILLIS_IN_HOUR;
       tz.setRawOffset(offsetMillis + dateNr);
       result.setTimeZone(tz);
       break;
     case TZM:
       inputFragmentStr =
           matchStringOrThrow(PATTERN_TWO_DIGITS_OR_LESS, params, formatTokenEnum);
       dateNr = parseInt(inputFragmentStr);
       tz = result.getTimeZone();
       offsetMillis = tz.getRawOffset();
       // purge hour
       offsetMillis = offsetMillis % MILLIS_IN_HOUR;
       tz.setRawOffset(dateNr * MILLIS_IN_HOUR + offsetMillis);
       result.setTimeZone(tz);
       break;
     case TZR:
       // Example: US/Pacific
       String s = params.getInputStr();
       tz = result.getTimeZone();
       for (String tzName : TimeZone.getAvailableIDs()) {
         int length = tzName.length();
         if (s.length() >= length && tzName.equalsIgnoreCase(s.substring(0, length))) {
           tz.setID(tzName);
           result.setTimeZone(tz);
           inputFragmentStr = tzName;
           break;
         }
       }
       break;
     case TZD:
       // Must correspond with TZR region. Example: PST (for US/Pacific
       // standard time)
       throwException(params, format("token '%s' not supported yet.", formatTokenEnum.name()));
       break;
     default:
       throw new IllegalArgumentException(
           format(
               "%s: Internal Error. Unhandled case: %s",
               this.getClass().getSimpleName(), formatTokenEnum));
   }
   params.remove(inputFragmentStr, formatTokenStr);
 }