/* * 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.addthis.hydra.data.filter.value; import java.util.SortedSet; import java.util.TreeSet; import com.addthis.basis.time.DTimeUnit; import com.addthis.basis.time.Dates; import com.addthis.basis.util.LessStrings; import com.addthis.codec.annotations.FieldConfig; import org.joda.time.DateTime; import org.joda.time.Interval; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; /** * This {@link AbstractValueFilter ValueFilter} <span class="hydra-summary">calculates the total numbers of days represented * in the input string</span>. * <p/> * <p>The input string is a sequence of items, where an item is either * a single day or a set of two days. The items are separated by {@link #dateRangeSep dateRangeSep}. * A set of two days is separated by {@link #dateSep dateSep}. The dates must be formatted * according to {@link #dateFormat dateFormat}. Each item in the sequence that is a single day * increments the output counter by one. Each item in the sequence that is a set of two days * increments the output counter by the number of days in between the two dates. * <p/> * <p>Example:</p> * <pre> * {from:"NUM_DAYS", data-range-length.dateFormat:"ddMMyy"} * </pre> * * @user-reference */ public class ValueFilterDateRangeLength extends StringFilter { /** * The input format using the * <a href="http://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html">DateTimeFormat</a>. * Default is "yyMMdd". */ @FieldConfig(codable = true) private String dateFormat = "yyMMdd"; /** * The separator in between (start date, end date) pairs. Default is "-" . */ @FieldConfig(codable = true) private String dateSep = "-"; /** * The separator in between items in the input sequence. Default is "," . */ @FieldConfig(codable = true) private String dateRangeSep = ","; /** * This field is not used. Do whatever you want with it. */ @FieldConfig(codable = true) private String diffUnit = "days"; public ValueFilterDateRangeLength() { } public ValueFilterDateRangeLength(String dateFormat, String dateSep, String diffUnit) { this.dateFormat = dateFormat; this.dateSep = dateSep; this.diffUnit = diffUnit; } @Override public String filter(String value) { if (value != null) { try { return Long.toString(countDays(value)); } catch (IllegalArgumentException e) { return null; } } return value; } protected Interval parseRange(String rangeString) { int sepIndex = rangeString.indexOf(dateSep); if (sepIndex <= 0 || sepIndex + 1 == rangeString.length()) { throw new IllegalArgumentException("Failed to parse date range: " + rangeString); } DateTimeFormatter dtf = DateTimeFormat.forPattern(dateFormat); DateTime beg = dtf.parseDateTime(rangeString.substring(0, sepIndex)); DateTime end = dtf.parseDateTime(rangeString.substring(sepIndex + 1, rangeString.length())); return new Interval(beg, end); } private String[] toArray(String dates) { return LessStrings.splitArray(dates, (dateRangeSep)); } protected int countDays(String dates) { DateTimeFormatter dtf = DateTimeFormat.forPattern(dateFormat); String[] datesSplit = toArray(dates); SortedSet<DateTime> dateSet = new TreeSet<>(); for (String strVal : datesSplit) { if (strVal.indexOf(dateSep) < 0) { dateSet.add(dtf.parseDateTime(strVal)); } else { Interval interval = parseRange(strVal); for (DateTime dt : Dates.iterableInterval(interval, DTimeUnit.DAY)) { dateSet.add(dt); } } } return dateSet.size(); } }