org.apache.drill.common.expression.fn.JodaDateValidator.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.drill.common.expression.fn.JodaDateValidator.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.drill.common.expression.fn;

import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;

import java.util.Comparator;
import java.util.Map;

import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_ABR_NAME_OF_MONTH;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_DAY_OF_MONTH;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_DAY_OF_WEEK;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_DAY_OF_YEAR;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_FULL_ERA_NAME;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_FULL_NAME_OF_DAY;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_HALFDAY_AM;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_HALFDAY_PM;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_HOUR_12_NAME;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_HOUR_12_OTHER_NAME;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_HOUR_24_NAME;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_ISO_1YEAR;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_ISO_2YEAR;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_ISO_3YEAR;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_ISO_4YEAR;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_ISO_WEEK_OF_YEAR;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_MILLISECOND_OF_MINUTE_NAME;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_MINUTE_OF_HOUR_NAME;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_MONTH;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_NAME_OF_DAY;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_NAME_OF_MONTH;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_SECOND_OF_MINUTE_NAME;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_WEEK_OF_YEAR;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.POSTGRES_YEAR;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.PREFIX_FM;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.PREFIX_FX;
import static org.apache.drill.common.expression.fn.JodaDateValidator.PostgresDateTimeConstant.PREFIX_TM;

public class JodaDateValidator {

    public enum PostgresDateTimeConstant {

        // patterns for replacing
        POSTGRES_FULL_NAME_OF_DAY(true, "day"), POSTGRES_DAY_OF_YEAR(false, "ddd"), POSTGRES_DAY_OF_MONTH(false,
                "dd"), POSTGRES_DAY_OF_WEEK(false, "d"), POSTGRES_NAME_OF_MONTH(true,
                        "month"), POSTGRES_ABR_NAME_OF_MONTH(true, "mon"), POSTGRES_YEAR(false,
                                "y"), POSTGRES_ISO_4YEAR(false, "iyyy"), POSTGRES_ISO_3YEAR(false,
                                        "iyy"), POSTGRES_ISO_2YEAR(false, "iy"), POSTGRES_ISO_1YEAR(false,
                                                "i"), POSTGRES_FULL_ERA_NAME(false, "ee"), POSTGRES_NAME_OF_DAY(
                                                        true, "dy"), POSTGRES_HOUR_12_NAME(false,
                                                                "hh"), POSTGRES_HOUR_12_OTHER_NAME(false,
                                                                        "hh12"), POSTGRES_HOUR_24_NAME(false,
                                                                                "hh24"), POSTGRES_MINUTE_OF_HOUR_NAME(
                                                                                        false,
                                                                                        "mi"), POSTGRES_SECOND_OF_MINUTE_NAME(
                                                                                                false,
                                                                                                "ss"), POSTGRES_MILLISECOND_OF_MINUTE_NAME(
                                                                                                        false,
                                                                                                        "ms"), POSTGRES_WEEK_OF_YEAR(
                                                                                                                false,
                                                                                                                "ww"), POSTGRES_ISO_WEEK_OF_YEAR(
                                                                                                                        false,
                                                                                                                        "iw"), POSTGRES_MONTH(
                                                                                                                                false,
                                                                                                                                "mm"), POSTGRES_HALFDAY_AM(
                                                                                                                                        false,
                                                                                                                                        "am"), POSTGRES_HALFDAY_PM(
                                                                                                                                                false,
                                                                                                                                                "pm"),

        // pattern modifiers for deleting
        PREFIX_FM(false, "fm"), PREFIX_FX(false, "fx"), PREFIX_TM(false, "tm");

        private final boolean hasCamelCasing;
        private final String name;

        PostgresDateTimeConstant(boolean hasCamelCasing, String name) {
            this.hasCamelCasing = hasCamelCasing;
            this.name = name;
        }

        public boolean hasCamelCasing() {
            return hasCamelCasing;
        }

        public String getName() {
            return name;
        }
    }

    private static final Map<PostgresDateTimeConstant, String> postgresToJodaMap = Maps
            .newTreeMap(new LengthDescComparator());

    public static final String POSTGRES_ESCAPE_CHARACTER = "\"";

    // jodaTime patterns
    public static final String JODA_FULL_NAME_OF_DAY = "EEEE";
    public static final String JODA_DAY_OF_YEAR = "D";
    public static final String JODA_DAY_OF_MONTH = "d";
    public static final String JODA_DAY_OF_WEEK = "e";
    public static final String JODA_NAME_OF_MONTH = "MMMM";
    public static final String JODA_ABR_NAME_OF_MONTH = "MMM";
    public static final String JODA_YEAR = "y";
    public static final String JODA_ISO_4YEAR = "xxxx";
    public static final String JODA_ISO_3YEAR = "xxx";
    public static final String JODA_ISO_2YEAR = "xx";
    public static final String JODA_ISO_1YEAR = "x";
    public static final String JODA_FULL_ERA_NAME = "G";
    public static final String JODA_NAME_OF_DAY = "E";
    public static final String JODA_HOUR_12_NAME = "h";
    public static final String JODA_HOUR_24_NAME = "H";
    public static final String JODA_MINUTE_OF_HOUR_NAME = "m";
    public static final String JODA_SECOND_OF_MINUTE_NAME = "ss";
    public static final String JODA_MILLISECOND_OF_MINUTE_NAME = "SSS";
    public static final String JODA_WEEK_OF_YEAR = "w";
    public static final String JODA_MONTH = "MM";
    public static final String JODA_HALFDAY = "aa";
    public static final String JODA_ESCAPE_CHARACTER = "'";
    public static final String EMPTY_STRING = "";

    static {
        postgresToJodaMap.put(POSTGRES_FULL_NAME_OF_DAY, JODA_FULL_NAME_OF_DAY);
        postgresToJodaMap.put(POSTGRES_DAY_OF_YEAR, JODA_DAY_OF_YEAR);
        postgresToJodaMap.put(POSTGRES_DAY_OF_MONTH, JODA_DAY_OF_MONTH);
        postgresToJodaMap.put(POSTGRES_DAY_OF_WEEK, JODA_DAY_OF_WEEK);
        postgresToJodaMap.put(POSTGRES_NAME_OF_MONTH, JODA_NAME_OF_MONTH);
        postgresToJodaMap.put(POSTGRES_ABR_NAME_OF_MONTH, JODA_ABR_NAME_OF_MONTH);
        postgresToJodaMap.put(POSTGRES_FULL_ERA_NAME, JODA_FULL_ERA_NAME);
        postgresToJodaMap.put(POSTGRES_NAME_OF_DAY, JODA_NAME_OF_DAY);
        postgresToJodaMap.put(POSTGRES_HOUR_12_NAME, JODA_HOUR_12_NAME);
        postgresToJodaMap.put(POSTGRES_HOUR_12_OTHER_NAME, JODA_HOUR_12_NAME);
        postgresToJodaMap.put(POSTGRES_HOUR_24_NAME, JODA_HOUR_24_NAME);
        postgresToJodaMap.put(POSTGRES_MINUTE_OF_HOUR_NAME, JODA_MINUTE_OF_HOUR_NAME);
        postgresToJodaMap.put(POSTGRES_SECOND_OF_MINUTE_NAME, JODA_SECOND_OF_MINUTE_NAME);
        postgresToJodaMap.put(POSTGRES_MILLISECOND_OF_MINUTE_NAME, JODA_MILLISECOND_OF_MINUTE_NAME);
        postgresToJodaMap.put(POSTGRES_WEEK_OF_YEAR, JODA_WEEK_OF_YEAR);
        postgresToJodaMap.put(POSTGRES_MONTH, JODA_MONTH);
        postgresToJodaMap.put(POSTGRES_HALFDAY_AM, JODA_HALFDAY);
        postgresToJodaMap.put(POSTGRES_HALFDAY_PM, JODA_HALFDAY);
        postgresToJodaMap.put(POSTGRES_ISO_WEEK_OF_YEAR, JODA_WEEK_OF_YEAR);
        postgresToJodaMap.put(POSTGRES_YEAR, JODA_YEAR);
        postgresToJodaMap.put(POSTGRES_ISO_1YEAR, JODA_ISO_1YEAR);
        postgresToJodaMap.put(POSTGRES_ISO_2YEAR, JODA_ISO_2YEAR);
        postgresToJodaMap.put(POSTGRES_ISO_3YEAR, JODA_ISO_3YEAR);
        postgresToJodaMap.put(POSTGRES_ISO_4YEAR, JODA_ISO_4YEAR);
        postgresToJodaMap.put(PREFIX_FM, EMPTY_STRING);
        postgresToJodaMap.put(PREFIX_FX, EMPTY_STRING);
        postgresToJodaMap.put(PREFIX_TM, EMPTY_STRING);
    }

    /**
     * Replaces all postgres patterns from {@param pattern},
     * available in postgresToJodaMap keys to jodaTime equivalents.
     *
     * @param pattern date pattern in postgres format
     * @return date pattern with replaced patterns in joda format
     */
    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();
    }

    /**
     * Length decreasing comparator.
     * Compares PostgresDateTimeConstant names by length, if they have the same length, compares them lexicographically.
     */
    private static class LengthDescComparator implements Comparator<PostgresDateTimeConstant> {

        public int compare(PostgresDateTimeConstant o1, PostgresDateTimeConstant o2) {
            int result = o2.getName().length() - o1.getName().length();
            if (result == 0) {
                return o1.getName().compareTo(o2.getName());
            }
            return result;
        }
    }

}