/*
* Copyright 2009-2012 by KNURT Systeme (http://www.knurt.de)
*
* Licensed under the Creative Commons License Attribution-NonCommercial-ShareAlike 3.0 Unported;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://creativecommons.org/licenses/by-nc-sa/3.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 de.knurt.fam.core.persistence.dao;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import org.springframework.dao.DataIntegrityViolationException;
import de.knurt.fam.core.aspects.logging.FamLog;
import de.knurt.fam.core.model.config.BookingStrategy;
import de.knurt.fam.core.model.config.Facility;
import de.knurt.fam.core.model.config.FacilityBookable;
import de.knurt.fam.core.model.persist.FacilityAvailability;
import de.knurt.fam.core.model.persist.User;
import de.knurt.fam.core.model.persist.booking.Booking;
import de.knurt.fam.core.model.persist.booking.BookingStatus;
import de.knurt.fam.core.model.persist.booking.Cancelation;
import de.knurt.fam.core.model.persist.booking.QueueBooking;
import de.knurt.fam.core.model.persist.booking.TimeBooking;
import de.knurt.fam.core.model.persist.document.Job;
import de.knurt.fam.core.persistence.dao.config.FacilityConfigDao;
import de.knurt.heinzelmann.util.time.SimpleTimeFrame;
import de.knurt.heinzelmann.util.time.TimeFrame;
/**
* A dao accessing stored {@link TimeBooking}s
*
* @author Daniel Oltmanns
* @since 0.20090625
*/
public abstract class BookingDao extends AbstractFamDao<Booking> {
/**
* return a list with uncancelled bookings and application for a given facility on a specific day.
*
* @param facility given
* @param c calendar representing the day
* @return a list with uncancelled bookings and application for a given facility on a specific day.
*/
public List<Booking> getAllUncanceledBookingsAndApplicationsOfDay(FacilityBookable facility, Calendar c) {
Calendar start = (Calendar) c.clone();
start.set(Calendar.HOUR_OF_DAY, 0);
start.set(Calendar.MINUTE, 0);
start.set(Calendar.SECOND, 0);
start.set(Calendar.MILLISECOND, 0);
Calendar end = (Calendar) start.clone();
end.add(Calendar.DAY_OF_YEAR, 1);
return this.getUncanceledBookingsAndApplicationsIn(facility, new SimpleTimeFrame(start, end));
}
public Booking getBooking(Job jobWithId) {
Booking result = null;
if (jobWithId != null && jobWithId.getJobId() != 0) {
result = this.getBookingWithId(jobWithId.getJobId());
}
return result;
}
/**
* return all bookings for the given facility that are overlapping the given timeframe. that are all bookings, where the date AND the end of booking
* IS NOT before OR after timeFrame. do not sort out bookings that are only applications.
*
* @param facility asking for
* @param timeFrame asking for
* @return all bookings for the given facility that are overlapping the given timeframe.
*/
public abstract List<Booking> getUncanceledBookingsAndApplicationsIn(FacilityBookable facility, TimeFrame timeFrame);
/**
* return all bookings for the given facility that are overlapping the given timeframe. do not sort out bookings that are only applications.
*
* @param facility asking for
* @param timeFrame booking shall be overlapping with
* @return all bookings for the given facility that are overlapping the given timeframe.
*/
public abstract List<Booking> getUncanceledBookingsAndApplicationsIn(FacilityBookable facility, FacilityAvailability timeFrame);
/**
* return all bookings for the given facility that are overlapping the given timeframe and starts in future. do not sort out bookings that are only
* applications.
*
* @see java.util.Date
* @see de.knurt.heinzelmann.util.time.IntervalTimeFrame
* @see BookingDao#getUncanceledBookingsAndApplicationsIn(de.knurt.fam.core.model.config.FacilityBookable,
* de.knurt.fam.core.model.persist.FacilityAvailability)
* @param facility asking for
* @param timeFrame booking shall be overlapping with
* @return all bookings for the given facility that are overlapping the given timeframe and starts in future.
*/
public abstract List<Booking> getUncanceledBookingsAndApplicationsStartingInFutureOverlapping(FacilityBookable facility, FacilityAvailability timeFrame);
/**
* return all bookings for the given facility that are overlapping the given timeframe. that are all bookings, where the date AND the end of booking
* IS NOT before OR after timeFrame. sort out bookings that are only applications.
*
* @param facility asking for
* @param timeFrame asking for
* @return all bookings for the given facility that are overlapping the given timeframe.
*/
public abstract List<Booking> getUncanceledBookingsWithoutApplicationsIn(FacilityBookable facility, TimeFrame timeFrame);
/**
* return all bookings and applications made by given user
*
* @param user given
* @return all bookings and applications made by given user
*/
public abstract List<Booking> getAllBookingsOfUser(User user);
/**
* simply return all + 1. this does not guerentee any unique ids!!!
*/
@Override
protected void setIdToNextId(Booking dataholder) {
dataholder.setId(this.getAll().size() + 1);
}
private String dataIntegrityMessage = null;
/** {@inheritDoc} */
@Override
public synchronized boolean insert(Booking dataholder) throws DataIntegrityViolationException {
dataholder.setSeton(new Date());
this.setIdToNextId(dataholder);
return super.insert(dataholder);
}
/**
* return true, if the given entry is not valid in saving.
*
* this is if:
* <ul>
* <li>{@link BookingStatus} is unset</li>
* <li>{@link TimeBooking} has invalid units (of time or capacity)</li>
* <li>{@link TimeBooking} is not free anymore (in case of client server thread problems)</li>
* </ul>
*
* updating none availables is valid. inserting not. this is why a boolean flag parameter is needed.
*
* @param booking to check
* @param onInsert true, if entry shall be inserted
* @return true, if the given entry is not valid in saving.
*/
@Override
protected boolean isDataIntegrityViolation(Booking booking, boolean onInsert) {
boolean result = false;
if (booking.getBookingStatus().isUnset()) {
result = true;
this.dataIntegrityMessage = "booking is unset";
}
if (result == false) {
if (booking.getCapacityUnits() < booking.getBookingRule().getMinBookableCapacityUnits(booking.getUser()) || booking.getCapacityUnits() > booking.getBookingRule().getMaxBookableCapacityUnits(booking.getUser())) {
result = true;
this.dataIntegrityMessage = "invalid units";
}
}
if (onInsert && result == false && booking.isAvailableForInsertion() == false && booking.isCanceled() == false) {
result = true;
this.dataIntegrityMessage = "not available anymore (" + booking.isAvailableForInsertion() + "/" + booking.isCanceled() + ")";
}
return result;
}
/** {@inheritDoc} */
@Override
protected void logAndThrowDataIntegrityViolationException(Booking entry) throws DataIntegrityViolationException {
String mess = "insert entry fail on booking " + entry + ". reason: " + dataIntegrityMessage + ".";
DataIntegrityViolationException ex = new DataIntegrityViolationException(mess);
FamLog.logException(BookingDao.class, ex, mess, 200906251009l);
throw ex;
}
/** {@inheritDoc} */
@Override
protected void logInsert(Booking entry) {
FamLog.info(String.format("insert booking id: %s; an: %s", entry.getId(), entry.getArticleNumber()), 200906251010l);
}
/** {@inheritDoc} */
@Override
protected void logUpdate(Booking entry) {
FamLog.info(String.format("update booking id: %s; an: %s", entry.getId(), entry.getArticleNumber()), 200906251011l);
}
/**
* return all bookings and applications for the given facility that are not cancelled and overlapping with today.
*
* @param facility given
* @return all bookings and applications for the given facility that are not cancelled and overlapping with today.
*/
public List<Booking> getAllUncanceledBookingsAndApplicationsOfToday(FacilityBookable facility) {
return this.getAllUncanceledBookingsAndApplicationsOfDay(facility, Calendar.getInstance());
}
/**
* return all bookings that are for one of the given facility. ignore all none-bookable facilities in given facilities. this means, if there is a
* booking for a meta-resource, return the bookings for it twice. e.g. peter booked a coffee machine of a mensa of a university. if
* "coffee machine", "mensa" and "university" is given in this method, return peters booking 3 times!
*
* @param facilities given
* @return all bookings that are for one of the given facility.
*/
public List<Booking> getAll(List<Facility> facilities) {
List<FacilityBookable> bookableFacilities = new ArrayList<FacilityBookable>();
for (Facility facility : facilities) {
bookableFacilities.addAll(FacilityConfigDao.getInstance().getBookableChildrenFacilities(facility.getKey()));
if (facility.isBookable()) {
bookableFacilities.add((FacilityBookable) facility);
}
}
return this.getAllIntern(bookableFacilities);
}
/**
* return all bookings that are for one of the given facility
*
* @param bookableFacilities given
* @return all bookings that are for one of the given facility
*/
public abstract List<Booking> getAllIntern(List<FacilityBookable> bookableFacilities);
/**
* return all bookings and applications that are not cancelled.
*
* @return all bookings and applications that are not cancelled.
*/
public abstract List<Booking> getAllUncanceled();
/**
* get all applications where the session starts in future.
*
* @return all applications where the session starts in future.
*/
public List<Booking> getAllUncanceledApplicationsNotMade() {
List<Booking> candidates = this.getAllUncanceled();
List<Booking> result = new ArrayList<Booking>();
for (Booking candidate : candidates) {
if (candidate.isApplication() && !candidate.sessionAlreadyMade()) {
result.add(candidate);
}
}
return result;
}
/**
* return the current length of the queue on a facility.
*
* @param facility the queue length is checked for
* @return the current length of the queue on a facility.
*/
public int getCurrentQueueLength(FacilityBookable facility) {
return this.getCurrentQueue(facility).size();
}
/**
* return the current position of the given booking in the queue. if the given queue is canceled are the session is already made, return null. if
* the session is running now, return 0. if the given booking is unknown (not saved yet), return queue length
*
* @param queueBasedBooking the position is given back.
* @return the current position of the given booking in the queue.
* @throws DataIntegrityViolationException
*/
public Integer getCurrentPositionInQueue(QueueBooking queueBasedBooking) throws DataIntegrityViolationException {
Integer result = null;
if (!queueBasedBooking.isCanceled() && !queueBasedBooking.sessionAlreadyMade() && queueBasedBooking.getSeton() != null) {
result = 0;
if (!queueBasedBooking.sessionAlreadyBegun()) {
List<QueueBooking> queue = this.getCurrentQueue(queueBasedBooking.getFacility());
try {
while (queue.get(result).getSeton().before(queueBasedBooking.getSeton()) || queue.get(result).getSeton().equals(queueBasedBooking.getSeton())) {
result++;
}
} catch (IndexOutOfBoundsException e) {
result = this.getCurrentQueueLength(queueBasedBooking.getFacility());
}
}
} else if (queueBasedBooking.getSeton() == null) { // unknown booking by
// now
result = this.getCurrentQueueLength(queueBasedBooking.getFacility());
}
return result;
}
/**
* return all bookings for the given facility as it is booked queue based. the result list must be sorted by date set on and DOES NOT CONTAIN
* canceled bookings. it contains the current session, that has already begun but not ended by now.
*
* @param facility bookings are for
* @return all bookings for the given facility as it is booked queue based.
*/
public abstract List<QueueBooking> getCurrentQueue(FacilityBookable facility);
/**
* check the availability situation for the given facility and cancel all overlapping bookings.
*
* @param facility to check
* @param cancelation to set in case of cancelations
*/
public void cancelOverlappingBookings(Facility facility, Cancelation cancelation) {
// IDEA v2 if the slot gets free again, the canceled bookings may be
// restored again
// else { // isCompletely free again
// free canceled bookings
// }
// cancel overlapping bookings
List<Booking> cancelBookingCandidates = new ArrayList<Booking>();
List<FacilityBookable> bookableFacilities = FacilityConfigDao.getInstance().getBookableChildrenFacilities(facility.getKey());
// get all depending bookable facilities
if (facility.isBookable()) {
bookableFacilities.add((FacilityBookable) facility);
}
// get all bookings of all bookable facilities
for (FacilityBookable bookableFacility : bookableFacilities) {
List<Booking> comingBookings = FamDaoProxy.bookingDao().getAllUncanceledBookingsAndApplicationsNotBegunYet(bookableFacility);
cancelBookingCandidates.addAll(comingBookings);
}
// cancel all bookings
for (Booking cancelBookingCandidate : cancelBookingCandidates) {
if (cancelBookingCandidate.getIdBookedInBookingStrategy() == BookingStrategy.TIME_BASED) {
if (((TimeBooking) cancelBookingCandidate).isApplicableToANotAvailableFacilityAvailability()) {
cancelBookingCandidate.cancel(cancelation);
cancelBookingCandidate.update();
}
}
}
}
/**
* return all booking requests that:
* <ul>
* <li>are for the given facility</li>
* <li>are not canceled</li>
* <li>not begun yet</li>
* </ul>
*
* @param bookableFacility bookings are for
* @return all booking requests that:
* <ul>
* <li>are for the given facility</li>
* <li>are not canceled</li>
* <li>not begun yet</li>
* </ul>
*/
public List<Booking> getAllUncanceledBookingsAndApplicationsNotBegunYet(FacilityBookable bookableFacility) {
List<Booking> candidates = this.getAllUncanceled(bookableFacility);
List<Booking> result = new ArrayList<Booking>();
for (Booking candidate : candidates) {
if (candidate.sessionAlreadyBegun() == false) {
result.add(candidate);
}
}
return result;
}
/**
* return all booking requests that:
* <ul>
* <li>are for the given facility</li>
* <li>are not canceled</li>
* <li>does not have complete the session</li>
* </ul>
*
* @param bookableFacility bookings are for
* @return all booking requests that:
* <ul>
* <li>are for the given facility</li>
* <li>are not canceled</li>
* <li>does not have complete the session</li>
* </ul>
*/
public List<Booking> getAllUncanceledBookingsAndApplicationsNotMadeYet(FacilityBookable bookableFacility) {
List<Booking> candidates = this.getAllUncanceled(bookableFacility);
List<Booking> result = new ArrayList<Booking>();
for (Booking candidate : candidates) {
if (candidate.sessionAlreadyMade() == false) {
result.add(candidate);
}
}
return result;
}
/**
* return all booking requests that:
* <ul>
* <li>are for the given facility</li>
* <li>are not canceled</li>
* </ul>
*
* @param bookableFacility bookings are for
* @return all booking requests that:
* <ul>
* <li>are for the given facility</li>
* <li>are not canceled</li>
* </ul>
*/
public List<Booking> getAllUncanceled(FacilityBookable bookableFacility) {
List<Booking> result = new ArrayList<Booking>();
List<Booking> candidates = this.getAll(bookableFacility);
for (Booking candidate : candidates) {
if (candidate.isCanceled() == false) {
result.add(candidate);
}
}
return result;
}
/**
* return all booking requests for the given facility.
*
* @param bookableFacility bookings are for
* @return all booking requests for the given facility.
*/
public List<Booking> getAll(FacilityBookable bookableFacility) {
List<Facility> tmp = new ArrayList<Facility>();
tmp.add(bookableFacility);
return this.getAll(tmp);
}
/**
* return all booking requests that:
* <ul>
* <li>are for the same facility as the given booking</li>
* <li>are not canceled</li>
* <li>are on the same session time frame as the given booking</li>
* </ul>
* <br />
* if the given booking is part of the database, it is included. otherwise not.
*
* @param booking to check
* @return all booking requests that:
* <ul>
* <li>are for the same facility as the given booking</li>
* <li>are not canceled</li>
* <li>are on the same session time frame as the given booking</li>
* </ul>
*/
public List<Booking> getAllUncanceledOverlapping(TimeBooking booking) {
return this.getUncanceledBookingsAndApplicationsIn(booking.getFacility(), booking.getSessionTimeFrame());
}
/**
* return the booking with the given id or null if no booking exists with this id.
*
* @param id to query for
* @return the booking with the given id or null if no booking exists with this id.
*/
public abstract Booking getBookingWithId(int id);
/**
* return a list with bookings of the user that are not cancelled and processed. if there are no bookings return an empty list.
*
* @param user the owner of the booking returned
* @return a list with bookings of the user that are not cancelled and processed.
*/
public abstract List<Booking> getAllUncanceledAndProcessed(User user);
/**
* return all current sessions of the given user. these are all time bookings hit the date "now".
*
* @param user owner of the sessions requested
* @return all current sessions of the given user
*/
public abstract List<TimeBooking> getCurrentSessions(User user);
}