com.metamx.common.Granularity.java Source code

Java tutorial

Introduction

Here is the source code for com.metamx.common.Granularity.java

Source

/*
 * Copyright 2011,2012 Metamarkets Group Inc.
 *
 * Licensed 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 com.metamx.common;

import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.Hours;
import org.joda.time.Interval;
import org.joda.time.Minutes;
import org.joda.time.Months;
import org.joda.time.MutableDateTime;
import org.joda.time.ReadableInterval;
import org.joda.time.ReadablePeriod;
import org.joda.time.Seconds;
import org.joda.time.Weeks;
import org.joda.time.Years;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 */
public enum Granularity {
    SECOND {
        final DateTimeFormatter defaultFormat = DateTimeFormat
                .forPattern("'y'=yyyy/'m'=MM/'d'=dd/'H'=HH/'M'=mm/'S'=ss");
        final DateTimeFormatter hiveFormat = DateTimeFormat.forPattern("'dt'=yyyy-MM-dd-HH-mm-ss");
        final DateTimeFormatter lowerDefaultFormat = DateTimeFormat
                .forPattern("'y'=yyyy/'m'=MM/'d'=dd/'h'=HH/'m'=mm/'s'=ss");

        @Override
        public DateTimeFormatter getFormatter(Formatter type) {
            switch (type) {
            case DEFAULT:
                return defaultFormat;
            case HIVE:
                return hiveFormat;
            case LOWER_DEFAULT:
                return lowerDefaultFormat;
            default:
                throw new IAE("There is no format for type %s at granularity %s", type, this.name());
            }
        }

        @Override
        public ReadablePeriod getUnits(int count) {
            return Seconds.seconds(count);
        }

        @Override
        public DateTime truncate(DateTime time) {
            final MutableDateTime mutableDateTime = time.toMutableDateTime();

            mutableDateTime.setMillisOfSecond(0);

            return mutableDateTime.toDateTime();
        }

        @Override
        public int numIn(ReadableInterval interval) {
            return Seconds.secondsIn(interval).getSeconds();
        }

        @Override
        public DateTime toDate(String filePath, Formatter formatter) {
            Integer[] vals = getDateValues(filePath, formatter);

            DateTime date = null;
            if (vals[1] != null && vals[2] != null && vals[3] != null && vals[4] != null && vals[5] != null
                    && vals[6] != null) {
                date = new DateTime(vals[1], vals[2], vals[3], vals[4], vals[5], vals[6], 0);
            }

            return date;
        }
    },
    MINUTE {
        final DateTimeFormatter defaultFormat = DateTimeFormat.forPattern("'y'=yyyy/'m'=MM/'d'=dd/'H'=HH/'M'=mm");
        final DateTimeFormatter hiveFormat = DateTimeFormat.forPattern("'dt'=yyyy-MM-dd-HH-mm");
        final DateTimeFormatter lowerDefaultFormat = DateTimeFormat
                .forPattern("'y'=yyyy/'m'=MM/'d'=dd/'h'=HH/'m'=mm");

        @Override
        public DateTimeFormatter getFormatter(Formatter type) {
            switch (type) {
            case DEFAULT:
                return defaultFormat;
            case HIVE:
                return hiveFormat;
            case LOWER_DEFAULT:
                return lowerDefaultFormat;
            default:
                throw new IAE("There is no format for type %s at granularity %s", type, this.name());
            }
        }

        @Override
        public ReadablePeriod getUnits(int count) {
            return Minutes.minutes(count);
        }

        @Override
        public DateTime truncate(DateTime time) {
            final MutableDateTime mutableDateTime = time.toMutableDateTime();

            mutableDateTime.setMillisOfSecond(0);
            mutableDateTime.setSecondOfMinute(0);

            return mutableDateTime.toDateTime();
        }

        @Override
        public int numIn(ReadableInterval interval) {
            return Minutes.minutesIn(interval).getMinutes();
        }

        @Override
        public DateTime toDate(String filePath, Formatter formatter) {
            Integer[] vals = getDateValues(filePath, formatter);

            DateTime date = null;
            if (vals[1] != null && vals[2] != null && vals[3] != null && vals[4] != null && vals[5] != null) {
                date = new DateTime(vals[1], vals[2], vals[3], vals[4], vals[5], 0, 0);
            }

            return date;
        }
    },
    FIVE_MINUTE {
        @Override
        public DateTimeFormatter getFormatter(Formatter type) {
            return MINUTE.getFormatter(type);
        }

        @Override
        public ReadablePeriod getUnits(int count) {
            return Minutes.minutes(count * 5);
        }

        @Override
        public DateTime truncate(DateTime time) {
            final MutableDateTime mutableDateTime = time.toMutableDateTime();

            mutableDateTime.setMillisOfSecond(0);
            mutableDateTime.setSecondOfMinute(0);
            mutableDateTime
                    .setMinuteOfHour(mutableDateTime.getMinuteOfHour() - (mutableDateTime.getMinuteOfHour() % 5));

            return mutableDateTime.toDateTime();
        }

        @Override
        public int numIn(ReadableInterval interval) {
            return Minutes.minutesIn(interval).getMinutes() / 5;
        }

        @Override
        public DateTime toDate(String filePath, Formatter formatter) {
            Integer[] vals = getDateValues(filePath, formatter);

            if (vals[1] != null && vals[2] != null && vals[3] != null && vals[4] != null && vals[5] != null) {
                return truncate(new DateTime(vals[1], vals[2], vals[3], vals[4], vals[5], 0, 0));
            }

            return null;
        }
    },
    TEN_MINUTE {
        @Override
        public DateTimeFormatter getFormatter(Formatter type) {
            return MINUTE.getFormatter(type);
        }

        @Override
        public ReadablePeriod getUnits(int count) {
            return Minutes.minutes(count * 10);
        }

        @Override
        public DateTime truncate(DateTime time) {
            final MutableDateTime mutableDateTime = time.toMutableDateTime();

            mutableDateTime.setMillisOfSecond(0);
            mutableDateTime.setSecondOfMinute(0);
            mutableDateTime
                    .setMinuteOfHour(mutableDateTime.getMinuteOfHour() - (mutableDateTime.getMinuteOfHour() % 10));

            return mutableDateTime.toDateTime();
        }

        @Override
        public int numIn(ReadableInterval interval) {
            return Minutes.minutesIn(interval).getMinutes() / 10;
        }

        @Override
        public DateTime toDate(String filePath, Formatter formatter) {
            Integer[] vals = getDateValues(filePath, formatter);

            if (vals[1] != null && vals[2] != null && vals[3] != null && vals[4] != null && vals[5] != null) {
                return truncate(new DateTime(vals[1], vals[2], vals[3], vals[4], vals[5], 0, 0));
            }

            return null;
        }
    },
    FIFTEEN_MINUTE {
        @Override
        public DateTimeFormatter getFormatter(Formatter type) {
            return MINUTE.getFormatter(type);
        }

        @Override
        public ReadablePeriod getUnits(int count) {
            return Minutes.minutes(count * 15);
        }

        @Override
        public DateTime truncate(DateTime time) {
            final MutableDateTime mutableDateTime = time.toMutableDateTime();

            mutableDateTime.setMillisOfSecond(0);
            mutableDateTime.setSecondOfMinute(0);
            mutableDateTime
                    .setMinuteOfHour(mutableDateTime.getMinuteOfHour() - (mutableDateTime.getMinuteOfHour() % 15));

            return mutableDateTime.toDateTime();
        }

        @Override
        public int numIn(ReadableInterval interval) {
            return Minutes.minutesIn(interval).getMinutes() / 15;
        }

        @Override
        public DateTime toDate(String filePath, Formatter formatter) {
            Integer[] vals = getDateValues(filePath, formatter);

            if (vals[1] != null && vals[2] != null && vals[3] != null && vals[4] != null && vals[5] != null) {
                return truncate(new DateTime(vals[1], vals[2], vals[3], vals[4], vals[5], 0, 0));
            }

            return null;
        }
    },
    HOUR {
        final DateTimeFormatter defaultFormat = DateTimeFormat.forPattern("'y'=yyyy/'m'=MM/'d'=dd/'H'=HH");
        final DateTimeFormatter hiveFormat = DateTimeFormat.forPattern("'dt'=yyyy-MM-dd-HH");
        final DateTimeFormatter lowerDefaultFormat = DateTimeFormat.forPattern("'y'=yyyy/'m'=MM/'d'=dd/'h'=HH");

        @Override
        public DateTimeFormatter getFormatter(Formatter type) {
            switch (type) {
            case DEFAULT:
                return defaultFormat;
            case HIVE:
                return hiveFormat;
            case LOWER_DEFAULT:
                return lowerDefaultFormat;
            default:
                throw new IAE("There is no format for type %s at granularity %s", type, this.name());
            }
        }

        @Override
        public ReadablePeriod getUnits(int n) {
            return Hours.hours(n);
        }

        @Override
        public DateTime truncate(DateTime time) {
            final MutableDateTime mutableDateTime = time.toMutableDateTime();

            mutableDateTime.setMillisOfSecond(0);
            mutableDateTime.setSecondOfMinute(0);
            mutableDateTime.setMinuteOfHour(0);

            return mutableDateTime.toDateTime();
        }

        @Override
        public int numIn(ReadableInterval interval) {
            return Hours.hoursIn(interval).getHours();
        }

        @Override
        public DateTime toDate(String filePath, Formatter formatter) {
            Integer[] vals = getDateValues(filePath, formatter);

            DateTime date = null;
            if (vals[1] != null && vals[2] != null && vals[3] != null && vals[4] != null) {
                date = new DateTime(vals[1], vals[2], vals[3], vals[4], 0, 0, 0);
            }

            return date;
        }
    },
    SIX_HOUR {
        @Override
        public DateTimeFormatter getFormatter(Formatter type) {
            return HOUR.getFormatter(type);
        }

        @Override
        public ReadablePeriod getUnits(int n) {
            return Hours.hours(n * 6);
        }

        @Override
        public DateTime truncate(DateTime time) {
            final MutableDateTime mutableDateTime = time.toMutableDateTime();

            mutableDateTime.setMillisOfSecond(0);
            mutableDateTime.setSecondOfMinute(0);
            mutableDateTime.setMinuteOfHour(0);
            mutableDateTime.setHourOfDay(mutableDateTime.getHourOfDay() - (mutableDateTime.getHourOfDay() % 6));

            return mutableDateTime.toDateTime();
        }

        @Override
        public int numIn(ReadableInterval interval) {
            return Hours.hoursIn(interval).getHours() / 6;
        }

        @Override
        public DateTime toDate(String filePath, Formatter formatter) {
            Integer[] vals = getDateValues(filePath, formatter);

            if (vals[1] != null && vals[2] != null && vals[3] != null && vals[4] != null) {
                return truncate(new DateTime(vals[1], vals[2], vals[3], vals[4], 0, 0, 0));
            }
            return null;
        }
    },
    DAY {
        final DateTimeFormatter defaultFormat = DateTimeFormat.forPattern("'y'=yyyy/'m'=MM/'d'=dd");
        final DateTimeFormatter hiveFormat = DateTimeFormat.forPattern("'dt'=yyyy-MM-dd");
        final DateTimeFormatter lowerDefaultFormat = DateTimeFormat.forPattern("'y'=yyyy/'m'=MM/'d'=dd");

        @Override
        public DateTimeFormatter getFormatter(Formatter type) {
            switch (type) {
            case DEFAULT:
                return defaultFormat;
            case HIVE:
                return hiveFormat;
            case LOWER_DEFAULT:
                return lowerDefaultFormat;
            default:
                throw new IAE("There is no format for type %s at granularity %s", type, this.name());
            }
        }

        @Override
        public ReadablePeriod getUnits(int n) {
            return Days.days(n);
        }

        @Override
        public DateTime truncate(DateTime time) {
            final MutableDateTime mutableDateTime = time.toMutableDateTime();

            mutableDateTime.setMillisOfDay(0);

            return mutableDateTime.toDateTime();
        }

        @Override
        public int numIn(ReadableInterval interval) {
            return Days.daysIn(interval).getDays();
        }

        @Override
        public DateTime toDate(String filePath, Formatter formatter) {
            Integer[] vals = getDateValues(filePath, formatter);

            DateTime date = null;
            if (vals[1] != null && vals[2] != null && vals[3] != null) {
                date = new DateTime(vals[1], vals[2], vals[3], 0, 0, 0, 0);
            }

            return date;
        }
    },
    WEEK {
        DateTimeFormatter defaultFormat = DateTimeFormat.forPattern("'y'=yyyy/'m'=MM/'d'=dd");
        DateTimeFormatter hiveFormat = DateTimeFormat.forPattern("'dt'=yyyy-MM-dd");
        DateTimeFormatter lowerDefaultFormat = DateTimeFormat.forPattern("'y'=yyyy/'m'=MM/'d'=dd");

        @Override
        public DateTimeFormatter getFormatter(Formatter type) {
            return DAY.getFormatter(type);
        }

        @Override
        public ReadablePeriod getUnits(int n) {
            return Weeks.weeks(n);
        }

        @Override
        public DateTime truncate(DateTime time) {
            final MutableDateTime mutableDateTime = time.toMutableDateTime();

            mutableDateTime.setMillisOfDay(0);
            mutableDateTime.setDayOfWeek(1);

            return mutableDateTime.toDateTime();
        }

        @Override
        public int numIn(ReadableInterval interval) {
            return Weeks.weeksIn(interval).getWeeks();
        }

        @Override
        public DateTime toDate(String filePath, Formatter formatter) {
            Integer[] vals = getDateValues(filePath, formatter);

            DateTime date = null;
            if (vals[1] != null && vals[2] != null && vals[3] != null) {
                date = truncate(new DateTime(vals[1], vals[2], vals[3], 0, 0, 0, 0));
            }

            return date;
        }
    },
    MONTH {
        final DateTimeFormatter defaultFormat = DateTimeFormat.forPattern("'y'=yyyy/'m'=MM");
        final DateTimeFormatter hiveFormat = DateTimeFormat.forPattern("'dt'=yyyy-MM");
        final DateTimeFormatter lowerDefaultFormat = DateTimeFormat.forPattern("'y'=yyyy/'m'=MM");

        @Override
        public DateTimeFormatter getFormatter(Formatter type) {
            switch (type) {
            case DEFAULT:
                return defaultFormat;
            case HIVE:
                return hiveFormat;
            case LOWER_DEFAULT:
                return lowerDefaultFormat;
            default:
                throw new IAE("There is no format for type %s at granularity %s", type, this.name());
            }
        }

        @Override
        public ReadablePeriod getUnits(int n) {
            return Months.months(n);
        }

        @Override
        public DateTime truncate(DateTime time) {
            final MutableDateTime mutableDateTime = time.toMutableDateTime();

            mutableDateTime.setMillisOfDay(0);
            mutableDateTime.setDayOfMonth(1);

            return mutableDateTime.toDateTime();
        }

        @Override
        public int numIn(ReadableInterval interval) {
            return Months.monthsIn(interval).getMonths();
        }

        @Override
        public DateTime toDate(String filePath, Formatter formatter) {
            Integer[] vals = getDateValues(filePath, formatter);

            DateTime date = null;
            if (vals[1] != null && vals[2] != null) {
                date = new DateTime(vals[1], vals[2], 1, 0, 0, 0, 0);
            }

            return date;
        }
    },
    YEAR {
        final DateTimeFormatter defaultFormat = DateTimeFormat.forPattern("'y'=yyyy");
        final DateTimeFormatter hiveFormat = DateTimeFormat.forPattern("'dt'=yyyy");
        final DateTimeFormatter lowerDefaultFormat = DateTimeFormat.forPattern("'y'=yyyy");

        @Override
        public DateTimeFormatter getFormatter(Formatter type) {
            switch (type) {
            case DEFAULT:
                return defaultFormat;
            case HIVE:
                return hiveFormat;
            case LOWER_DEFAULT:
                return lowerDefaultFormat;
            default:
                throw new IAE("There is no format for type %s at granularity %s", type, this.name());
            }
        }

        @Override
        public ReadablePeriod getUnits(int n) {
            return Years.years(n);
        }

        @Override
        public DateTime truncate(DateTime time) {
            final MutableDateTime mutableDateTime = time.toMutableDateTime();

            mutableDateTime.setMillisOfDay(0);
            mutableDateTime.setDayOfMonth(1);
            mutableDateTime.setMonthOfYear(1);

            return mutableDateTime.toDateTime();
        }

        @Override
        public int numIn(ReadableInterval interval) {
            return Years.yearsIn(interval).getYears();
        }

        @Override
        public DateTime toDate(String filePath, Formatter formatter) {
            Integer[] vals = getDateValues(filePath, formatter);

            DateTime date = null;
            if (vals[1] != null) {
                date = new DateTime(vals[1], 1, 1, 0, 0, 0, 0);
            }

            return date;
        }
    };

    // Default patterns for parsing paths.
    protected final Pattern defaultPathPattern = Pattern.compile(
            "^.*[Yy]=(\\d{4})/(?:[Mm]=(\\d{2})/(?:[Dd]=(\\d{2})/(?:[Hh]=(\\d{2})/(?:[Mm]=(\\d{2})/(?:[Ss]=(\\d{2})/)?)?)?)?)?.*$");
    protected final Pattern hivePathPattern = Pattern
            .compile("^.*dt=(\\d{4})(?:-(\\d{2})(?:-(\\d{2})(?:-(\\d{2})(?:-(\\d{2})(?:-(\\d{2})?)?)?)?)?)?/.*$");

    // Abstract functions that individual enum's need to implement for the strategy.
    public abstract DateTimeFormatter getFormatter(Formatter type);

    public abstract ReadablePeriod getUnits(int n);

    public abstract DateTime truncate(DateTime time);

    public abstract int numIn(ReadableInterval interval);

    public abstract DateTime toDate(String filePath, Formatter formatter);

    public DateTime toDate(String filePath) {
        return toDate(filePath, Formatter.DEFAULT);
    }

    // Used by the toDate implementations.
    protected final Integer[] getDateValues(String filePath, Formatter formatter) {
        Pattern pattern = defaultPathPattern;
        switch (formatter) {
        case DEFAULT:
        case LOWER_DEFAULT:
            break;
        case HIVE:
            pattern = hivePathPattern;
            break;
        default:
            throw new IAE("Format %s not supported", formatter);
        }

        Matcher matcher = pattern.matcher(filePath);

        Integer[] vals = new Integer[7];
        if (matcher.matches()) {
            for (int i = 1; i <= matcher.groupCount(); i++) {
                vals[i] = (matcher.group(i) != null) ? Integer.parseInt(matcher.group(i)) : null;
            }
        }

        return vals;
    }

    // Strategy Functions
    public final DateTimeFormatter getFormatter(String type) {
        return getFormatter(Formatter.valueOf(type.toUpperCase()));
    }

    public final DateTime increment(DateTime time) {
        return time.plus(getUnits(1));
    }

    public final DateTime increment(DateTime time, int count) {
        return time.plus(getUnits(count));
    }

    public final DateTime decrement(DateTime time) {
        return time.minus(getUnits(1));
    }

    public final DateTime decrement(DateTime time, int count) {
        return time.minus(getUnits(count));
    }

    public final String toPath(DateTime time) {
        return toPath(time, "default");
    }

    public final String toPath(DateTime time, String type) {
        return toPath(time, Formatter.valueOf(type.toUpperCase()));
    }

    public final String toPath(DateTime time, Formatter type) {
        return getFormatter(type).print(time);
    }

    /**
     * Return a granularity-sized Interval containing a particular DateTime.
     */
    public final Interval bucket(DateTime t) {
        DateTime start = truncate(t);
        return new Interval(start, increment(start));
    }

    /**
     * Round out Interval such that it becomes granularity-aligned and nonempty.
     */
    public final Interval widen(Interval interval) {
        final DateTime start = truncate(interval.getStart());
        final DateTime end;

        if (interval.getEnd().equals(start)) {
            // Empty with aligned start/end; expand into a granularity-sized interval
            end = increment(start);
        } else if (truncate(interval.getEnd()).equals(interval.getEnd())) {
            // Non-empty with aligned end; keep the same end
            end = interval.getEnd();
        } else {
            // Non-empty with non-aligned end; push it out
            end = increment(truncate(interval.getEnd()));
        }

        return new Interval(start, end);
    }

    // Iterable functions and classes.
    public Iterable<Interval> getIterable(final DateTime start, final DateTime end) {
        return getIterable(new Interval(start, end));
    }

    public Iterable<Interval> getIterable(final Interval input) {
        return new IntervalIterable(input);
    }

    public Iterable<Interval> getReverseIterable(final DateTime start, final DateTime end) {
        return getReverseIterable(new Interval(start, end));
    }

    public Iterable<Interval> getReverseIterable(final Interval input) {
        return new ReverseIntervalIterable(input);
    }

    public class IntervalIterable implements Iterable<Interval> {
        private final Interval inputInterval;

        public IntervalIterable(Interval inputInterval) {
            this.inputInterval = inputInterval;
        }

        @Override
        public Iterator<Interval> iterator() {
            return new IntervalIterator(inputInterval);
        }

    }

    public class ReverseIntervalIterable implements Iterable<Interval> {
        private final Interval inputInterval;

        public ReverseIntervalIterable(Interval inputInterval) {
            this.inputInterval = inputInterval;
        }

        @Override
        public Iterator<Interval> iterator() {
            return new ReverseIntervalIterator(inputInterval);
        }

    }

    public class IntervalIterator implements Iterator<Interval> {
        private final Interval inputInterval;

        private DateTime currStart;
        private DateTime currEnd;

        public IntervalIterator(Interval inputInterval) {
            this.inputInterval = inputInterval;

            currStart = truncate(inputInterval.getStart());
            currEnd = increment(currStart);
        }

        @Override
        public boolean hasNext() {
            return currStart.isBefore(inputInterval.getEnd());
        }

        @Override
        public Interval next() {
            if (!hasNext()) {
                throw new NoSuchElementException("There are no more intervals");
            }
            Interval retVal = new Interval(currStart, currEnd);

            currStart = currEnd;
            currEnd = increment(currStart);

            return retVal;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    public class ReverseIntervalIterator implements Iterator<Interval> {
        private final Interval inputInterval;

        private DateTime currStart;
        private DateTime currEnd;

        public ReverseIntervalIterator(Interval inputInterval) {
            this.inputInterval = inputInterval;

            currEnd = inputInterval.getEnd();
            currStart = decrement(currEnd);

        }

        @Override
        public boolean hasNext() {
            return currEnd.isAfter(inputInterval.getStart());
        }

        @Override
        public Interval next() {
            if (!hasNext()) {
                throw new NoSuchElementException("There are no more intervals");
            }
            Interval retVal = new Interval(currStart, currEnd);

            currEnd = currStart;
            currStart = decrement(currEnd);

            return retVal;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    public enum Formatter {
        DEFAULT, HIVE, LOWER_DEFAULT
    }
}