class JodaDateValidator {

  public static String toJodaFormat(String pattern) {
    // replaces escape character for text delimiter
    StringBuilder builder = new StringBuilder(pattern.replaceAll(POSTGRES_ESCAPE_CHARACTER, JODA_ESCAPE_CHARACTER));

    int start = 0;    // every time search of postgres token in pattern will start from this index.
    int minPos;       // min position of the longest postgres token
    do {
      // finds first value with max length
      minPos = builder.length();
      PostgresDateTimeConstant firstMatch = null;
      for (PostgresDateTimeConstant postgresPattern : postgresToJodaMap.keySet()) {
        // keys sorted in length decreasing
        // at first search longer tokens to consider situation where some tokens are the parts of large tokens
        // example: if pattern contains a token "DDD", token "DD" would be skipped, as a part of "DDD".
        int pos;
        // some tokens can't be in upper camel casing, so we ignore them here.
        // example: DD, DDD, MM, etc.
        if (postgresPattern.hasCamelCasing()) {
          // finds postgres tokens in upper camel casing
          // example: Month, Mon, Day, Dy, etc.
          pos = builder.indexOf(StringUtils.capitalize(postgresPattern.getName()), start);
          if (pos >= 0 && pos < minPos) {
            firstMatch = postgresPattern;
            minPos = pos;
            if (minPos == start) {
              break;
            }
          }
        }
        // finds postgres tokens in lower casing
        pos = builder.indexOf(postgresPattern.getName().toLowerCase(), start);
        if (pos >= 0 && pos < minPos) {
          firstMatch = postgresPattern;
          minPos = pos;
          if (minPos == start) {
            break;
          }
        }
        // finds postgres tokens in upper casing
        pos = builder.indexOf(postgresPattern.getName().toUpperCase(), start);
        if (pos >= 0 && pos < minPos) {
          firstMatch = postgresPattern;
          minPos = pos;
          if (minPos == start) {
            break;
          }
        }
      }
      // replaces postgres token, if found and it does not escape character
      if (minPos < builder.length() && firstMatch != null) {
        String jodaToken = postgresToJodaMap.get(firstMatch);
        // checks that token is not a part of escape sequence
        if (StringUtils.countMatches(builder.subSequence(0, minPos), JODA_ESCAPE_CHARACTER) % 2 == 0) {
          int offset = minPos + firstMatch.getName().length();
          builder.replace(minPos, offset, jodaToken);
          start = minPos + jodaToken.length();
        } else {
          int endEscapeCharacter = builder.indexOf(JODA_ESCAPE_CHARACTER, minPos);
          if (endEscapeCharacter >= 0) {
            start = endEscapeCharacter;
          } else {
            break;
          }
        }
      }
    } while (minPos < builder.length());
    return builder.toString();
  }

}