Java tutorial
/* * Licensed to Metamarkets Group Inc. (Metamarkets) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. Metamarkets 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 io.druid.java.util.common.granularity; import com.fasterxml.jackson.annotation.JsonCreator; import com.google.common.collect.Lists; import com.google.common.primitives.Longs; import io.druid.java.util.common.Cacheable; import io.druid.java.util.common.DateTimes; import io.druid.java.util.common.IAE; import io.druid.java.util.common.StringUtils; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.Interval; import org.joda.time.format.DateTimeFormatter; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; public abstract class Granularity implements Cacheable { /** * Default patterns for parsing paths. */ private static final Pattern defaultPathPattern = Pattern.compile( "^.*[Yy]=(\\d{4})/(?:[Mm]=(\\d{2})/(?:[Dd]=(\\d{2})/(?:[Hh]=(\\d{2})/(?:[Mm]=(\\d{2})/(?:[Ss]=(\\d{2})/)?)?)?)?)?.*$"); private static final Pattern hivePathPattern = Pattern .compile("^.*dt=(\\d{4})(?:-(\\d{2})(?:-(\\d{2})(?:-(\\d{2})(?:-(\\d{2})(?:-(\\d{2})?)?)?)?)?)?/.*$"); @JsonCreator public static Granularity fromString(String str) { return GranularityType.valueOf(StringUtils.toUpperCase(str)).getDefaultGranularity(); } /** * simple merge strategy on query granularity that checks if all are equal or else * returns null. this can be improved in future but is good enough for most use-cases. */ public static Granularity mergeGranularities(List<Granularity> toMerge) { if (toMerge == null || toMerge.size() == 0) { return null; } Granularity result = toMerge.get(0); for (int i = 1; i < toMerge.size(); i++) { if (!Objects.equals(result, toMerge.get(i))) { return null; } } return result; } public static List<Granularity> granularitiesFinerThan(final Granularity gran0) { final List<Granularity> retVal = Lists.newArrayList(); final DateTime origin = (gran0 instanceof PeriodGranularity) ? ((PeriodGranularity) gran0).getOrigin() : null; final DateTimeZone tz = (gran0 instanceof PeriodGranularity) ? ((PeriodGranularity) gran0).getTimeZone() : null; for (GranularityType gran : GranularityType.values()) { /** * All and None are excluded b/c when asked to give all granularities finer * than "TEN_MINUTE", you want the answer to be "FIVE_MINUTE, MINUTE and SECOND" * it doesn't make sense to include ALL or None to be part of this. */ if (gran == GranularityType.ALL || gran == GranularityType.NONE) { continue; } final Granularity segmentGranularity = gran.create(origin, tz); final long segmentGranularityDurationMillis = segmentGranularity.bucket(DateTimes.EPOCH) .toDurationMillis(); final long gran0DurationMillis = gran0.bucket(DateTimes.EPOCH).toDurationMillis(); if (segmentGranularityDurationMillis <= gran0DurationMillis) { retVal.add(segmentGranularity); } } retVal.sort((g1, g2) -> { long duration1 = g2.bucket(DateTimes.EPOCH).toDurationMillis(); long duration2 = g1.bucket(DateTimes.EPOCH).toDurationMillis(); return Longs.compare(duration1, duration2); }); return retVal; } public abstract DateTimeFormatter getFormatter(Formatter type); public abstract DateTime increment(DateTime time); public abstract DateTime decrement(DateTime time); public abstract DateTime bucketStart(DateTime time); public abstract DateTime toDate(String filePath, Formatter formatter); public DateTime bucketEnd(DateTime time) { return increment(bucketStart(time)); } public DateTime toDateTime(long offset) { return DateTimes.utc(offset); } public DateTime toDate(String filePath) { return toDate(filePath, Formatter.DEFAULT); } public final String toPath(DateTime time) { return getFormatter(Formatter.DEFAULT).print(time); } /** * Return a granularity-sized Interval containing a particular DateTime. */ public final Interval bucket(DateTime t) { DateTime start = bucketStart(t); return new Interval(start, increment(start)); } // Used by the toDate implementations. 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); // The size is "7" b/c this array contains standard // datetime field values namely: // year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, // and index 0 is unused. 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; } public Iterable<Interval> getIterable(final Interval input) { return new IntervalIterable(input); } public enum Formatter { DEFAULT, HIVE, LOWER_DEFAULT } private class IntervalIterable implements Iterable<Interval> { private final Interval inputInterval; private IntervalIterable(Interval inputInterval) { this.inputInterval = inputInterval; } @Override public Iterator<Interval> iterator() { return new IntervalIterator(inputInterval); } } private class IntervalIterator implements Iterator<Interval> { private final Interval inputInterval; private DateTime currStart; private DateTime currEnd; private IntervalIterator(Interval inputInterval) { this.inputInterval = inputInterval; currStart = bucketStart(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(); } } }