com.ikanow.aleph2.core.shared.utils.TimeSliceDirUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.ikanow.aleph2.core.shared.utils.TimeSliceDirUtils.java

Source

/*******************************************************************************
 * Copyright 2015, The IKANOW Open Source Project.
 *
 * 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.ikanow.aleph2.core.shared.utils;

import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;

import org.apache.commons.lang.time.DateUtils;

import scala.Tuple2;
import scala.Tuple3;

import com.ikanow.aleph2.data_model.objects.data_analytics.AnalyticThreadJobBean.AnalyticThreadJobInputConfigBean;
import com.ikanow.aleph2.data_model.utils.Patterns;
import com.ikanow.aleph2.data_model.utils.TimeUtils;
import com.ikanow.aleph2.data_model.utils.Tuples;

import fj.data.Validation;

/** A few utils to handle time sliced dirs
 * @author alex
 */
public class TimeSliceDirUtils {

    /** Given a pair of (optional) human readable strings (which are assumed to refer to the past)
     *  returns a pair of optional dates
     * @param input_config
     * @return
     */
    public static Tuple2<Optional<Date>, Optional<Date>> getQueryTimeRange(
            final AnalyticThreadJobInputConfigBean input_config, final Date now) {
        Function<Optional<String>, Optional<Date>> parseDate = maybe_date -> maybe_date
                .map(datestr -> TimeUtils.getSchedule(datestr, Optional.of(now))).filter(res -> res.isSuccess())
                .map(res -> res.success())
                // OK so this wants to be backwards in time always...
                .map(date -> {
                    if (date.getTime() > now.getTime()) {
                        final long diff = date.getTime() - now.getTime();
                        return Date.from(now.toInstant().minusMillis(diff));
                    } else
                        return date;
                });

        final Optional<Date> tmin = parseDate.apply(Optional.ofNullable(input_config.time_min()));
        final Optional<Date> tmax = parseDate.apply(Optional.ofNullable(input_config.time_max()));

        return Tuples._2T(tmin, tmax);
    }

    /** The complement to annotateTimedDirectories - returns directories that don't have associated times
     * @param dir_listing
     */
    public static Stream<String> getUntimedDirectories(final Stream<String> dir_listing) {
        return candidateTimedDirectories(dir_listing).filter(dir_datestr_date -> dir_datestr_date._3().isFail())
                .map(dir_datestr_date -> dir_datestr_date._1());
    }

    /** Takes a stream of strings (paths) and adds the date range
     * @param dir_listing
     * @return
     */
    public static Stream<Tuple3<String, Date, Date>> annotateTimedDirectories(final Stream<String> dir_listing) {
        //(first get to last element in path, then if it ends _<date> then grab <date>, else assume it's the whole thing)
        return candidateTimedDirectories(dir_listing).filter(dir_datestr_date -> dir_datestr_date._3().isSuccess())
                .map(dir_datestr_date -> {
                    final Optional<Tuple2<String, ChronoUnit>> info = TimeUtils
                            .getFormatInfoFromDateString(dir_datestr_date._2());
                    return Tuples._3T(dir_datestr_date._1(), info, dir_datestr_date._3());
                }).filter(dir_datestr_date -> dir_datestr_date._2().isPresent())
                .map(dir_datestr_date -> Tuples._3T(dir_datestr_date._1(), dir_datestr_date._3().success(),
                        adjustTime(Date.from(dir_datestr_date._3().success().toInstant()),
                                dir_datestr_date._2().get()._2())));
    }

    /** Internal utility
     * @param dir_listing
     * @return
     */
    private static Stream<Tuple3<String, String, Validation<String, Date>>> candidateTimedDirectories(
            final Stream<String> dir_listing) {
        return dir_listing.map(dir -> dir.endsWith("/") ? dir.substring(0, dir.length() - 1) : dir)
                .map(dir -> Tuples._2T(dir, dir.lastIndexOf("/"))) // if not present, returns -1 which means the substring below grabs the entire thing
                .map(dir_date -> Tuples._2T(dir_date._1(), dir_date._1().substring(1 + dir_date._2())))
                .map(dir_date -> Tuples._3T(dir_date._1(), dir_date._2(), dir_date._2().lastIndexOf("_"))) // if not present, returns -1 which means the substring below grabs the entire thing
                .map(dir_date_index -> { // ugly case where ends in a segment (with or without date)
                    if ((dir_date_index._2().length() - dir_date_index._3()) <= 4) { //ie 3 chars + 1 for '_', means it can't be a date so must be a segment
                        final String new_date = dir_date_index._1().substring(0, dir_date_index._3()); //(up to including the last _)
                        return Tuples._3T(dir_date_index._1(), new_date, new_date.lastIndexOf("_"));
                    } else
                        return dir_date_index;
                })
                .map(dir_date_index -> Tuples._2T(dir_date_index._1(),
                        dir_date_index._2().substring(1 + dir_date_index._3())))
                .map(dir_date -> Tuples._3T(dir_date._1(), dir_date._2(),
                        TimeUtils.getDateFromSuffix(dir_date._2())));
    }

    /** Filters out non matching directories
     * @param in
     * @param filter
     * @return
     */
    public static Stream<String> filterTimedDirectories(final Stream<Tuple3<String, Date, Date>> in,
            final Tuple2<Optional<Date>, Optional<Date>> filter) {
        return in.filter(t3 -> filter._1().map(tmin -> { //lower bound
            return tmin.getTime() < t3._3().getTime(); // just has to be smaller than the largest time in the group
        }).orElse(true)).filter(t3 -> filter._2().map(tmax -> { //lower bound
            return tmax.getTime() >= t3._2().getTime(); // just has to be larger than the smallest time in the group
        }).orElse(true)).map(t3 -> t3._1());
    }

    /** Low level util because java8 time "plus" is odd
     * @param to_adjust
     * @param increment
     * @return
     */
    private static Date adjustTime(Date to_adjust, ChronoUnit increment) {
        return Patterns.match(increment).<Date>andReturn()
                .when(t -> t == ChronoUnit.SECONDS, __ -> DateUtils.addSeconds(to_adjust, 1))
                .when(t -> t == ChronoUnit.MINUTES, __ -> DateUtils.addMinutes(to_adjust, 1))
                .when(t -> t == ChronoUnit.HOURS, __ -> DateUtils.addHours(to_adjust, 1))
                .when(t -> t == ChronoUnit.DAYS, __ -> DateUtils.addDays(to_adjust, 1))
                .when(t -> t == ChronoUnit.WEEKS, __ -> DateUtils.addWeeks(to_adjust, 1))
                .when(t -> t == ChronoUnit.MONTHS, __ -> DateUtils.addMonths(to_adjust, 1))
                .when(t -> t == ChronoUnit.YEARS, __ -> DateUtils.addYears(to_adjust, 1)).otherwiseAssert();
    }
}