/*
* Copyright (C) 2014 Indeed 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.indeed.imhotep.sql.parser;
import com.google.common.base.Strings;
import org.joda.time.Period;
import javax.annotation.Nullable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author vladimir
*/
public class PeriodParser {
/**
* Returns a JodaTime Period object representing the provided string period value.
* Only first symbol of each field tag is mandatory, the rest of the tag is optional. e.g. 1d = 1 day
* Spacing between the numbers and tags is optional. e.g. 1d = 1 d
* Having a tag with no number implies quantity of 1. e.g. d = 1d
* 'ago' suffix is optional.
* Commas can optionally separate the period parts.
*/
@Nullable
public static Period parseString(String value) {
final String cleanedValue = Strings.nullToEmpty(value).toLowerCase();
Matcher matcher = relativeDatePattern.matcher(cleanedValue);
if(!matcher.matches()) {
return null;
}
int years = getValueFromMatch(matcher, 1);
int months = getValueFromMatch(matcher, 2);
int weeks = getValueFromMatch(matcher, 3);
int days = getValueFromMatch(matcher, 4);
int hours = getValueFromMatch(matcher, 5);
int minutes = getValueFromMatch(matcher, 6);
int seconds = getValueFromMatch(matcher, 7);
return new Period(years, months, weeks, days, hours, minutes, seconds, 0);
}
private static final Pattern relativeDatePattern = Pattern.compile(
"(\\s*(\\d+)?\\s*y(?:ear)?s?\\s*,?\\s*)?" +
"(\\s*(\\d+)?\\s*mo(?:nth)?s?\\s*,?\\s*)?" +
"(\\s*(\\d+)?\\s*w(?:eek)?s?\\s*,?\\s*)?" +
"(\\s*(\\d+)?\\s*d(?:ay)?s?\\s*,?\\s*)?" +
"(\\s*(\\d+)?\\s*h(?:our)?s?\\s*,?\\s*)?" +
"(\\s*(\\d+)?\\s*m(?:inute)?s?\\s*,?\\s*)?" +
"(\\s*(\\d+)?\\s*s(?:econd)?s?\\s*)?" +
"(?:ago)?\\s*"
);
private static int getValueFromMatch(Matcher matcher, int i) {
final String fieldMatch = matcher.group((i * 2) - 1);
if(Strings.isNullOrEmpty(fieldMatch)) {
return 0;
}
String value = matcher.group(i * 2);
return tryParseInt(value, 1); // if we have a field match then we treat it as 1 by default
}
private static int tryParseInt(String val, int def) {
int retVal;
try {
retVal = Integer.parseInt(val);
} catch (NumberFormatException nfe) {
retVal = def;
}
return retVal;
}
}