001/**
002 * Copyright 2005-2017 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.kim.api.common.history;
017
018import org.joda.time.DateTime;
019import org.kuali.rice.core.api.criteria.OrderByField;
020import org.kuali.rice.core.api.criteria.OrderDirection;
021import org.kuali.rice.core.api.criteria.Predicate;
022import org.kuali.rice.core.api.criteria.QueryByCriteria;
023
024import java.sql.Timestamp;
025import java.util.ArrayList;
026import java.util.List;
027
028import static org.kuali.rice.core.api.criteria.PredicateFactory.*;
029
030public final class HistoryQueryUtils {
031
032    private HistoryQueryUtils() {
033        throw new UnsupportedOperationException();
034    }
035
036    public static QueryByCriteria historyQuery(String id, DateTime asOfDate) {
037        Timestamp asOfTimestamp = new Timestamp(asOfDate.getMillis());
038        Predicate predicate =
039            and(
040                equal("id", id),
041                and(
042                    or(isNull("activeFromDateValue"), lessThanOrEqual("activeFromDateValue", asOfTimestamp)),
043                    or(isNull("activeToDateValue"), greaterThan("activeToDateValue", asOfTimestamp))
044                )
045            );
046
047        QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create();
048        criteria.setMaxResults(Integer.valueOf(1));
049        criteria.setPredicates(predicate);
050        List<OrderByField> orderByFields = new ArrayList<OrderByField>();
051        orderByFields.add(OrderByField.Builder.create("activeFromDateValue", OrderDirection.DESCENDING).build());
052        orderByFields.add(OrderByField.Builder.create("historyId", OrderDirection.DESCENDING).build());
053        criteria.setOrderByFields(orderByFields);
054
055        return criteria.build();
056    }
057
058    public static QueryByCriteria futureRecordQuery(String id, DateTime asOfDate) {
059        Timestamp asOfTimestamp = new Timestamp(asOfDate.getMillis());
060        Predicate predicate =
061                and(
062                    equal("id", id),
063                    or(isNull("activeFromDateValue"), greaterThan("activeFromDateValue", asOfTimestamp))
064                );
065
066        QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create();
067        criteria.setMaxResults(Integer.valueOf(1));
068        criteria.setPredicates(predicate);
069        List<OrderByField> orderByFields = new ArrayList<OrderByField>();
070        orderByFields.add(OrderByField.Builder.create("activeFromDateValue", OrderDirection.DESCENDING).build());
071        orderByFields.add(OrderByField.Builder.create("historyId", OrderDirection.DESCENDING).build());
072        criteria.setOrderByFields(orderByFields);
073
074        return criteria.build();
075    }
076
077    public static Predicate between(String startField,String endField, DateTime asOfDate) {
078        // the precision of this check should be to the second, not milliseconds, so we want to chop off any
079        // milliseconds and do a ceiling of our seconds. Sometimes changes are made in near real time after a record
080        // becomes activated or inactivated so we want to have the best result possible if they are still within the
081        // same second, so we essentially always round up to ensure that this check will never fail in high throughput
082        // environments
083        asOfDate = asOfDate.secondOfDay().roundCeilingCopy();
084        return
085                and(
086                    or(isNull(startField), lessThanOrEqual(startField, asOfDate.toDate())),
087                    or(isNull(endField), greaterThan(endField, asOfDate.toDate()))
088                );
089    }
090}