Package org.asynchttpclient.date

Source Code of org.asynchttpclient.date.RFC2616DateParser

/*
* Copyright (c) 2014 AsyncHttpClient Project. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package org.asynchttpclient.date;

import org.asynchttpclient.date.RFC2616Date.Builder;

/**
* A parser for <a href="http://tools.ietf.org/html/rfc2616#section-3.3">RFC2616
* Date format</a>.
*
* @author slandelle
*/
public class RFC2616DateParser {

    private final String string;
    private final int offset;
    private final int length;

    /**
     * @param string a string that will be fully parsed
     */
    public RFC2616DateParser(String string) {
        this(string, 0, string.length());
    }

    /**
     * @param string the string to be parsed
     * @param offset the offset where to start parsing
     * @param length the number of chars to parse
     */
    public RFC2616DateParser(String string, int offset, int length) {

        if (string.length() + offset < length)
            throw new IllegalArgumentException("String length doesn't match offset and length");

        this.string = string;
        this.offset = offset;
        this.length = length;
    }

    private static class Tokens {
        public final int[] starts;
        public final int[] ends;
        public final int length;

        public Tokens(int[] starts, int[] ends, int length) {
            this.starts = starts;
            this.ends = ends;
            this.length = length;
        }
    }

    private Tokens tokenize() {

        int[] starts = new int[8];
        int[] ends = new int[8];
        boolean inToken = false;
        int tokenCount = 0;

        int end = offset + length;
        for (int i = offset; i < end; i++) {

            char c = string.charAt(i);
            if (c == ' ' || c == ',' || c == '-' || c == ':') {
                if (inToken) {
                    ends[tokenCount++] = i;
                    inToken = false;
                }
            } else if (!inToken) {
                starts[tokenCount] = i;
                inToken = true;
            }
        }

        // finish lastToken
        if (inToken == true)
            ends[tokenCount++] = end;

        return new Tokens(starts, ends, tokenCount);
    }

    /**
     * @param validate if validation is to be enabled of non-critical elements,
     *            such as day of week and timezone
     * @return null is the string is not a valid RFC2616 date
     */
    public RFC2616Date parse() {

        Tokens tokens = tokenize();

        if (tokens.length != 7 && tokens.length != 8)
            return null;

        // 1st token is ignored: ignore day of week
        // 8th token is ignored: supposed to always be GMT

        if (isDigit(string.charAt(tokens.starts[1])))
            return buildDate(tokens);
        else
            return buildANSICDate(tokens);
    }

    private RFC2616Date buildDate(Tokens tokens) {

        // Sun, 06 Nov 1994 08:49:37 GMT

        Builder dateBuilder = new Builder();

        if (isValidDayOfMonth(tokens.starts[1], tokens.ends[1], dateBuilder) && //
                isValidMonth(tokens.starts[2], tokens.ends[2], dateBuilder) && //
                isValidYear(tokens.starts[3], tokens.ends[3], dateBuilder) && //
                isValidHour(tokens.starts[4], tokens.ends[4], dateBuilder) && //
                isValidMinuteSecond(tokens.starts[5], tokens.ends[5], dateBuilder, true) && //
                isValidMinuteSecond(tokens.starts[6], tokens.ends[6], dateBuilder, false)) {
            return dateBuilder.build();
        }

        return null;
    }

    private RFC2616Date buildANSICDate(Tokens tokens) {

        // Sun Nov 6 08:49:37 1994

        Builder dateBuilder = new Builder();

        if (isValidMonth(tokens.starts[1], tokens.ends[1], dateBuilder) && //
                isValidDayOfMonth(tokens.starts[2], tokens.ends[2], dateBuilder) && //
                isValidHour(tokens.starts[3], tokens.ends[3], dateBuilder) && //
                isValidMinuteSecond(tokens.starts[4], tokens.ends[4], dateBuilder, true) && //
                isValidMinuteSecond(tokens.starts[5], tokens.ends[5], dateBuilder, false) && //
                isValidYear(tokens.starts[6], tokens.ends[6], dateBuilder)) {
            return dateBuilder.build();
        }

        return null;
    }

    private boolean isValid1DigitDayOfMonth(char c0, Builder dateBuilder) {
        if (isDigit(c0)) {
            dateBuilder.setDayOfMonth(getNumericValue(c0));
            return true;
        }
        return false;
    }

    private boolean isValid2DigitsDayOfMonth(char c0, char c1, Builder dateBuilder) {
        if (isDigit(c0) && isDigit(c1)) {
            int i0 = getNumericValue(c0);
            int i1 = getNumericValue(c1);
            int day = i0 * 10 + i1;
            if (day <= 31) {
                dateBuilder.setDayOfMonth(day);
                return true;
            }
        }
        return false;
    }

    private boolean isValidDayOfMonth(int start, int end, Builder dateBuilder) {

        int tokenLength = end - start;

        if (tokenLength == 1) {
            char c0 = string.charAt(start);
            return isValid1DigitDayOfMonth(c0, dateBuilder);

        } else if (tokenLength == 2) {
            char c0 = string.charAt(start);
            char c1 = string.charAt(start + 1);
            return isValid2DigitsDayOfMonth(c0, c1, dateBuilder);
        }
        return false;
    }

    private boolean isValidJanuaryJuneJuly(char c0, char c1, char c2, Builder dateBuilder) {
        if (c0 == 'J' || c0 == 'j')
            if (c1 == 'a' || c1 == 'A') {
                if (c2 == 'n' || c2 == 'N') {
                    dateBuilder.setJanuary();
                    return true;
                }
            } else if (c1 == 'u' || c1 == 'U') {
                if (c2 == 'n' || c2 == 'N') {
                    dateBuilder.setJune();
                    return true;
                } else if (c2 == 'l' || c2 == 'L') {
                    dateBuilder.setJuly();
                    return true;
                }
            }
        return false;
    }

    private boolean isValidFebruary(char c0, char c1, char c2, Builder dateBuilder) {
        if ((c0 == 'F' || c0 == 'f') && (c1 == 'e' || c1 == 'E') && (c2 == 'b' || c2 == 'B')) {
            dateBuilder.setFebruary();
            return true;
        }
        return false;
    }

    private boolean isValidMarchMay(char c0, char c1, char c2, Builder dateBuilder) {
        if ((c0 == 'M' || c0 == 'm') && (c1 == 'a' || c1 == 'A')) {
            if (c2 == 'r' || c2 == 'R') {
                dateBuilder.setMarch();
                return true;
            } else if (c2 == 'y' || c2 == 'M') {
                dateBuilder.setMay();
                return true;
            }
        }
        return false;
    }

    private boolean isValidAprilAugust(char c0, char c1, char c2, Builder dateBuilder) {
        if (c0 == 'A' || c0 == 'a')
            if ((c1 == 'p' || c1 == 'P') && (c2 == 'r' || c2 == 'R')) {
                dateBuilder.setApril();
                return true;
            } else if ((c1 == 'u' || c1 == 'U') && (c2 == 'g' || c2 == 'G')) {
                dateBuilder.setAugust();
                return true;
            }
        return false;
    }

    private boolean isValidSeptember(char c0, char c1, char c2, Builder dateBuilder) {
        if ((c0 == 'S' || c0 == 's') && (c1 == 'e' || c1 == 'E') && (c2 == 'p' || c2 == 'P')) {
            dateBuilder.setSeptember();
            return true;
        }
        return false;
    }

    private boolean isValidOctober(char c0, char c1, char c2, Builder dateBuilder) {
        if ((c0 == 'O' || c0 == 'o') && (c1 == 'c' || c1 == 'C') && (c2 == 't' || c2 == 'T')) {
            dateBuilder.setOctobre();
            return true;
        }
        return false;
    }

    private boolean isValidNovember(char c0, char c1, char c2, Builder dateBuilder) {
        if ((c0 == 'N' || c0 == 'n') && (c1 == 'o' || c1 == 'O') && (c2 == 'v' || c2 == 'V')) {
            dateBuilder.setNovembre();
            return true;
        }
        return false;
    }

    private boolean isValidDecember(char c0, char c1, char c2, Builder dateBuilder) {
        if (c0 == 'D' || c0 == 'd')
            if (c1 == 'e' || c1 == 'E') {
                if (c2 == 'c' || c2 == 'C') {
                    dateBuilder.setDecember();
                    return true;
                }
            }
        return false;
    }

    private boolean isValidMonth(int start, int end, Builder dateBuilder) {

        if (end - start != 3)
            return false;

        char c0 = string.charAt(start);
        char c1 = string.charAt(start + 1);
        char c2 = string.charAt(start + 2);

        return isValidJanuaryJuneJuly(c0, c1, c2, dateBuilder) || //
                isValidFebruary(c0, c1, c2, dateBuilder) || //
                isValidMarchMay(c0, c1, c2, dateBuilder) || //
                isValidAprilAugust(c0, c1, c2, dateBuilder) || //
                isValidSeptember(c0, c1, c2, dateBuilder) || //
                isValidOctober(c0, c1, c2, dateBuilder) || //
                isValidNovember(c0, c1, c2, dateBuilder) || //
                isValidDecember(c0, c1, c2, dateBuilder);
    }

    private boolean isValid2DigitsYear(char c0, char c1, Builder dateBuilder) {
        if (isDigit(c0) && isDigit(c1)) {
            int i0 = getNumericValue(c0);
            int i1 = getNumericValue(c1);
            int year = i0 * 10 + i1;
            year = year < 70 ? year + 2000 : year + 1900;

            return setValidYear(year, dateBuilder);
        }
        return false;
    }

    private boolean isValid4DigitsYear(char c0, char c1, char c2, char c3, Builder dateBuilder) {
        if (isDigit(c0) && isDigit(c1) && isDigit(c2) && isDigit(c3)) {
            int i0 = getNumericValue(c0);
            int i1 = getNumericValue(c1);
            int i2 = getNumericValue(c2);
            int i3 = getNumericValue(c3);
            int year = i0 * 1000 + i1 * 100 + i2 * 10 + i3;

            return setValidYear(year, dateBuilder);
        }
        return false;
    }

    private boolean setValidYear(int year, Builder dateBuilder) {
        if (year >= 1601) {
            dateBuilder.setYear(year);
            return true;
        }
        return false;
    }

    private boolean isValidYear(int start, int end, Builder dateBuilder) {

        int length = end - start;

        if (length == 2) {
            char c0 = string.charAt(start);
            char c1 = string.charAt(start + 1);
            return isValid2DigitsYear(c0, c1, dateBuilder);

        } else if (length == 4) {
            char c0 = string.charAt(start);
            char c1 = string.charAt(start + 1);
            char c2 = string.charAt(start + 2);
            char c3 = string.charAt(start + 3);
            return isValid4DigitsYear(c0, c1, c2, c3, dateBuilder);
        }

        return false;
    }

    private boolean isValid1DigitHour(char c0, Builder dateBuilder) {
        if (isDigit(c0)) {
            int hour = getNumericValue(c0);
            dateBuilder.setHour(hour);
            return true;
        }
        return false;
    }

    private boolean isValid2DigitsHour(char c0, char c1, Builder dateBuilder) {
        if (isDigit(c0) && isDigit(c1)) {
            int i0 = getNumericValue(c0);
            int i1 = getNumericValue(c1);
            int hour = i0 * 10 + i1;
            if (hour <= 24) {
                dateBuilder.setHour(hour);
                return true;
            }
        }
        return false;
    }

    private boolean isValidHour(int start, int end, Builder dateBuilder) {

        int length = end - start;

        if (length == 1) {
            char c0 = string.charAt(start);
            return isValid1DigitHour(c0, dateBuilder);

        } else if (length == 2) {
            char c0 = string.charAt(start);
            char c1 = string.charAt(start + 1);
            return isValid2DigitsHour(c0, c1, dateBuilder);
        }
        return false;
    }

    private boolean isValid1DigitMinuteSecond(char c0, Builder dateBuilder, boolean minuteOrSecond) {
        if (isDigit(c0)) {
            int value = getNumericValue(c0);
            if (minuteOrSecond)
                dateBuilder.setMinute(value);
            else
                dateBuilder.setSecond(value);
            return true;
        }
        return false;
    }

    private boolean isValid2DigitsMinuteSecond(char c0, char c1, Builder dateBuilder, boolean minuteOrSecond) {
        if (isDigit(c0) && isDigit(c1)) {
            int i0 = getNumericValue(c0);
            int i1 = getNumericValue(c1);
            int value = i0 * 10 + i1;
            if (value <= 60) {
                if (minuteOrSecond)
                    dateBuilder.setMinute(value);
                else
                    dateBuilder.setSecond(value);
                return true;
            }
        }
        return false;
    }

    private boolean isValidMinuteSecond(int start, int end, Builder dateBuilder, boolean minuteOrSecond) {

        int length = end - start;

        if (length == 1) {
            char c0 = string.charAt(start);
            return isValid1DigitMinuteSecond(c0, dateBuilder, minuteOrSecond);

        } else if (length == 2) {
            char c0 = string.charAt(start);
            char c1 = string.charAt(start + 1);
            return isValid2DigitsMinuteSecond(c0, c1, dateBuilder, minuteOrSecond);
        }
        return false;
    }

    private boolean isDigit(char c) {
        return c >= '0' && c <= '9';
    }

    private int getNumericValue(char c) {
        return (int) c - 48;
    }
}
TOP

Related Classes of org.asynchttpclient.date.RFC2616DateParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.