org.apereo.portal.events.aggr.AggregationIntervalHelperImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apereo.portal.events.aggr.AggregationIntervalHelperImpl.java

Source

/**
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo 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 the following location:
 *
 *   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 org.apereo.portal.events.aggr;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apereo.portal.events.aggr.dao.DateDimensionDao;
import org.apereo.portal.events.aggr.dao.IEventAggregationManagementDao;
import org.apereo.portal.events.aggr.dao.TimeDimensionDao;
import org.apereo.portal.events.aggr.dao.jpa.AcademicTermDetailImpl;
import org.joda.time.DateMidnight;
import org.joda.time.DateTime;
import org.joda.time.LocalTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author Eric Dalquist
 * @version $Revision: 18025 $
 */
@Service
public class AggregationIntervalHelperImpl implements AggregationIntervalHelper {
    protected final Log logger = LogFactory.getLog(this.getClass());

    private TimeDimensionDao timeDimensionDao;
    private DateDimensionDao dateDimensionDao;
    private IEventAggregationManagementDao eventAggregationManagementDao;

    @Autowired
    public void setEventAggregationManagementDao(IEventAggregationManagementDao eventAggregationManagementDao) {
        this.eventAggregationManagementDao = eventAggregationManagementDao;
    }

    @Autowired
    public void setTimeDimensionDao(TimeDimensionDao timeDimensionDao) {
        this.timeDimensionDao = timeDimensionDao;
    }

    @Autowired
    public void setDateDimensionDao(DateDimensionDao dateDimensionDao) {
        this.dateDimensionDao = dateDimensionDao;
    }

    @Override
    public List<DateTime> getIntervalStartDateTimesBetween(AggregationInterval interval, DateTime start,
            DateTime end) {
        return getIntervalStartDateTimesBetween(interval, start, end, -1);
    }

    public int intervalsBetween(AggregationInterval interval, DateTime start, DateTime end) {
        //For intervals that support native determination
        if (interval.isSupportsDetermination()) {
            return interval.determineIntervalsBetween(start, end);
        }

        //Special handling for intervals that don't support determination

        if (interval == AggregationInterval.ACADEMIC_TERM) {
            //Since terms can have gaps all terms must be checked

            //Find the first term than is in the range via binary search
            final List<AcademicTermDetail> terms = getAcademicTermsAfter(start);

            //Count all the terms that are in the range, breaking the loop on the first non-matching term
            int count = 0;
            for (final AcademicTermDetail academicTerm : terms) {
                final DateMidnight termStart = academicTerm.getStart();
                if (end.isAfter(termStart) && !start.isAfter(termStart)) {
                    count++;
                } else if (count > 0) {
                    //getAcademicTermDetails returns the list in order, after at least one match has been found
                    //a term that doesn't match means no more matches will be found
                    break;
                }
            }
            return count;
        }

        //Fallback for any other interval type that needs explicit iteration
        AggregationIntervalInfo nextInterval = this.getIntervalInfo(interval, start);

        int count = 0;
        while (nextInterval.getStart().isBefore(end)) {
            //Needed to make sure we don't count a partial first interval
            if (!start.isAfter(nextInterval.getStart())) {
                count++;
            }

            nextInterval = this.getIntervalInfo(interval, nextInterval.getEnd());
        }

        return count;
    }

    @Override
    public List<DateTime> getIntervalStartDateTimesBetween(AggregationInterval interval, DateTime start,
            DateTime end, int maxTimes) {
        if (interval.isSupportsDetermination()) {
            //Get the interval count for the date-time field type and verify it is in the valid range.
            final int intervals = interval.determineIntervalsBetween(start, end);
            verifyIntervalCount(start, end, maxTimes, intervals);

            //Result list
            final List<DateTime> result = new ArrayList<DateTime>(intervals);

            //Check if first interval in the range
            DateTime intervalStart = interval.determineStart(start);
            if (!intervalStart.isBefore(start)) {
                result.add(intervalStart);
            }

            //Move one step forward in the range
            DateTime intervalEnd = interval.determineEnd(intervalStart);
            intervalStart = interval.determineStart(intervalEnd);

            //Step through the interval start/end values to build the full list
            while (intervalStart.isBefore(end)) {
                result.add(intervalStart);

                intervalEnd = interval.determineEnd(intervalStart);
                intervalStart = interval.determineStart(intervalEnd);
            }

            return result;
        }

        //Special handling for intervals that don't support determination
        if (interval == AggregationInterval.ACADEMIC_TERM) {
            //Since terms can have gaps all terms must be checked
            final List<AcademicTermDetail> terms = getAcademicTermsAfter(start);

            //Use all the terms that are in the range, breaking the loop on the first non-matching term
            final List<DateTime> result = new ArrayList<DateTime>(terms.size());
            for (final AcademicTermDetail academicTerm : terms) {
                final DateMidnight termStart = academicTerm.getStart();
                if (end.isAfter(termStart) && !start.isAfter(termStart)) {
                    result.add(start);
                } else if (!result.isEmpty()) {
                    //getAcademicTermDetails returns the list in order, after at least one match has been found
                    //a term that doesn't match means no more matches will be found
                    break;
                }
            }
            return result;
        }

        //Fallback for any other interval type that needs explicit iteration
        AggregationIntervalInfo nextInterval = this.getIntervalInfo(interval, start);

        final List<DateTime> result = new ArrayList<DateTime>();
        while (nextInterval.getStart().isBefore(end)) {
            //Needed to make sure we don't count a partial first interval
            if (!start.isAfter(nextInterval.getStart())) {
                result.add(nextInterval.getStart());

                if (maxTimes > 0 && result.size() > maxTimes) {
                    throw new IllegalArgumentException("There more than " + result.size() + " intervals between "
                            + start + " and " + end + " which is more than the specified maximum of " + maxTimes);
                }
            }

            nextInterval = this.getIntervalInfo(interval, nextInterval.getEnd());
        }
        return result;
    }

    @Override
    public AggregationIntervalInfo getIntervalInfo(AggregationInterval interval, DateTime date) {
        //Chop off everything below the minutes (seconds, millis)
        final DateTime instant = date.minuteOfHour().roundFloorCopy();

        final DateTime start, end;
        switch (interval) {
        case CALENDAR_QUARTER: {
            final List<QuarterDetail> quartersDetails = this.eventAggregationManagementDao.getQuartersDetails();
            final QuarterDetail quarterDetail = EventDateTimeUtils.findDateRangeSorted(instant, quartersDetails);
            start = quarterDetail.getStartDateMidnight(date).toDateTime();
            end = quarterDetail.getEndDateMidnight(date).toDateTime();
            break;
        }
        case ACADEMIC_TERM: {
            final List<AcademicTermDetail> academicTermDetails = this.eventAggregationManagementDao
                    .getAcademicTermDetails();
            final AcademicTermDetail academicTermDetail = EventDateTimeUtils.findDateRangeSorted(date,
                    academicTermDetails);
            if (academicTermDetail == null) {
                return null;
            }

            start = academicTermDetail.getStart().toDateTime();
            end = academicTermDetail.getEnd().toDateTime();

            break;
        }
        default: {
            start = interval.determineStart(instant);
            end = interval.determineEnd(start);
        }
        }

        final LocalTime startTime = start.toLocalTime();
        final TimeDimension startTimeDimension = this.timeDimensionDao.getTimeDimensionByTime(startTime);

        final DateMidnight startDateMidnight = start.toDateMidnight();
        final DateDimension startDateDimension = this.dateDimensionDao.getDateDimensionByDate(startDateMidnight);

        return new AggregationIntervalInfo(interval, start, end, startDateDimension, startTimeDimension);
    }

    /**
     * Return a sorted list of AcademicTermDetail objects where the the first element of the list where the first element
     * is the first term that starts after the specified start DateTime. 
     */
    protected List<AcademicTermDetail> getAcademicTermsAfter(DateTime start) {
        final List<AcademicTermDetail> terms = this.eventAggregationManagementDao.getAcademicTermDetails();
        final int index = Collections.binarySearch(terms,
                new AcademicTermDetailImpl(start.toDateMidnight(), start.plusDays(1).toDateMidnight(), ""));
        if (index > 0) {
            return terms.subList(index, terms.size());
        } else if (index < 0) {
            return terms.subList(-(index + 1), terms.size());
        }
        return terms;
    }

    protected void verifyIntervalCount(DateTime start, DateTime end, int maxTimes, final int intervals) {
        if (maxTimes > 0 && intervals > maxTimes) {
            throw new IllegalArgumentException("There are " + intervals + " intervals between " + start + " and "
                    + end + " which is more than the specified maximum of " + maxTimes);
        }
    }
}