/* * 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.model.persist; 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.model.config.Facility; import de.knurt.fam.core.model.persist.booking.Cancelation; import de.knurt.fam.core.persistence.dao.FacilityDao; import de.knurt.fam.core.persistence.dao.FamDaoProxy; import de.knurt.fam.core.persistence.dao.config.FacilityConfigDao; import de.knurt.heinzelmann.util.adapter.ComparableInDifferentWays; import de.knurt.heinzelmann.util.adapter.ViewableObject; import de.knurt.heinzelmann.util.query.Identificable; import de.knurt.heinzelmann.util.time.IntervalTimeFrame; import de.knurt.heinzelmann.util.time.SimpleIntervalTimeFrame; import de.knurt.heinzelmann.util.time.TimeFrame; /** * a facility is generaly available or not. this class describes a time frame that is full, free or maybe available. * * @author Daniel Oltmanns * @since 0.20090424 */ public class FacilityAvailability extends StoreableDeletableAbstract implements ComparableInDifferentWays, Cloneable, IntervalTimeFrame, Availability, ViewableObject, Comparable<FacilityAvailability>, Identificable { /** * flag for unrestricted availability. */ public static final int COMPLETE_AVAILABLE = 0; /** * flag for not availability in general. flag in case of facility is outside opening hours or special events. */ public static final int GENERAL_NOT_AVAILABLE = 1; /** * this is to mark a facility that is limited available in general. this is NOT the case if any bookings are there with applications and there is no * possibility to put this in by now but temporary (without persistence). * * @see FacilityDao#isDataIntegrityViolation(de.knurt.fam.core.model.persist.FacilityAvailability) */ public static final int MAYBE_AVAILABLE = 2; /** * flag for not availability because of bookings. flag in case of facility is booked up. */ public static final int BOOKED_NOT_AVAILABLE = 3; /** * flag for not availability because of maintenance. flag in case of facility is maintained. */ public static final int MAINTENANCE_NOT_AVAILABLE = 4; public static final int SUDDEN_FAILURE_NOT_AVAILABLE = 5; /** * flag for "booking must not start here". */ public static final int BOOKING_MUST_NOT_START_HERE = 6; private String facilityKey; private Date timeStampSet; private String usernameSetThis; private Integer id; /** * set this to compare the availability by the base time frame. this is the default comparing modus. * * @see #getTimeStampSet() * @see #getComparingModus() * @see #compareTo(de.knurt.fam.core.model.persist.FacilityAvailability) */ public static final int COMPARE_BY_BASE_PERIOD_OF_TIME = 0; /** * set this to compare the availability by the date it was set. * * @see #getTimeStampSet() * @see #getComparingModus() * @see #compareTo(de.knurt.fam.core.model.persist.FacilityAvailability) */ public static final int COMPARE_BY_DATE_SET = 1; private int comparingModus = COMPARE_BY_BASE_PERIOD_OF_TIME; /** * construct availability for given facility and time frame. after constructing the availability is set to <code>null</code>. * * @param facilityKey representing the facility the availability is set for. * @param timeFrame base period of time the availability is set for. */ public FacilityAvailability(String facilityKey, TimeFrame timeFrame) { this(facilityKey, timeFrame.getCalendarStart(), timeFrame.getCalendarEnd()); } /** * return the date when the base period of time starts. * * @see IntervalTimeFrame#getBasePeriodOfTime() * @see #getBasePeriodOfTime() * @return the date when the base period of time starts. */ public Date getStartOfBasePeriodOfTime() { return this.getBasePeriodOfTime() == null ? null : this.getBasePeriodOfTime().getDateStart(); } /** * set the date when the base period of time starts. * * @see IntervalTimeFrame#getBasePeriodOfTime() * @see #getBasePeriodOfTime() * @param start the date when the base period of time starts. */ public void setStartOfBasePeriodOfTime(Date start) { this.createIntervalTimeFrameNotSet(); this.getBasePeriodOfTime().setStart(start.getTime()); } /** * set the date when the base period of time ends. * * @see IntervalTimeFrame#getBasePeriodOfTime() * @see #getBasePeriodOfTime() * @param end the date when the base period of time ends. */ public void setEndOfBasePeriodOfTime(Date end) { this.createIntervalTimeFrameNotSet(); this.getBasePeriodOfTime().setEnd(end.getTime()); } /** * return the date when the base period of time ends. * * @see IntervalTimeFrame#getBasePeriodOfTime() * @see #getBasePeriodOfTime() * @return the date when the base period of time ends. */ public Date getEndOfBasePeriodOfTime() { return this.getBasePeriodOfTime() == null ? null : this.getBasePeriodOfTime().getDateEnd(); } /** * @return the userSetThis */ public User getUserSetThis() { return FamDaoProxy.userDao().getUserFromUsername(this.usernameSetThis); } /** * set the user that is responsible for this availability. it can be set directly (if an operator set a maintenance) or indirectly (if a user * overbook another booking). * * @param user the user that is responsible for this availability. */ public void setUserSetThis(User user) { this.usernameSetThis = user.getUsername(); } /** * set username of user that set the availability for the facility. * * @param usernameSetThis username of user that set the availability for the facility. */ public void setUsernameSetThis(String usernameSetThis) { this.usernameSetThis = usernameSetThis; } /** * return the user that is responsible for this availability. it can be set directly (if an operator set a maintenance) or indirectly (if a user * overbook another booking). * * @return the user that is responsible for this availability. */ public String getUsernameSetThis() { return this.usernameSetThis; } /** * return true, if the given integer is one of the class constants * * @param availability to check * @return true, if the given integer is one of static constants */ public static boolean isValidAvailability(int availability) { return availability >= 0 && availability <= 6; } private Integer available; private String notice; private IntervalTimeFrame intervalTimeFrame; /** * constructur mainly to construct examples for queries. call super default constructor. after constructing the availability is set to * <code>null</code>. */ public FacilityAvailability() { super(); } /** * return true, if a notice exist. notice must not be null or empty. * * @return true, if a notice exist. */ public boolean hasNotice() { return this.isNotice(this.notice); } /** * construct availability for given facility and time frame. after constructing the availability is set to <code>null</code>. * * @param facilityKey representing the facility the availability is set for. * @param start of base period of time. * @param end of base period of time. */ public FacilityAvailability(String facilityKey, Calendar start, Calendar end) { this.intervalTimeFrame = new SimpleIntervalTimeFrame(start, end); this.facilityKey = facilityKey; this.timeStampSet = new Date(); } /** {@inheritDoc} */ @Override public boolean insert() throws DataIntegrityViolationException { boolean result = FamDaoProxy.facilityDao().insert(this); Cancelation c = new Cancelation(this.getUserSetThis(), Cancelation.REASON_NOT_AVAILABLE_IN_GENERAL); FamDaoProxy.bookingDao().cancelOverlappingBookings(this.getFacility(), c); return result; } /** {@inheritDoc} */ @Override public boolean delete() throws DataIntegrityViolationException { boolean result = FamDaoProxy.facilityDao().delete(this); Cancelation c = new Cancelation(this.getUserSetThis(), Cancelation.REASON_NOT_AVAILABLE_IN_GENERAL); FamDaoProxy.bookingDao().cancelOverlappingBookings(this.getFacility(), c); return result; } /** {@inheritDoc} */ @Override public boolean update() throws DataIntegrityViolationException { boolean result = FamDaoProxy.facilityDao().update(this); Cancelation c = new Cancelation(this.getUserSetThis(), Cancelation.REASON_NOT_AVAILABLE_IN_GENERAL); FamDaoProxy.bookingDao().cancelOverlappingBookings(this.getFacility(), c); return result; } /** * @return the facilityKey */ public String getFacilityKey() { return facilityKey; } /** * @return the facilityAvailabilityId */ public long getFacilityAvailabilityId() { return this.id.longValue(); } /** * @param facilityAvailabilityId the facilityAvailabilityId to set */ public void setFacilityAvailabilityId(long facilityAvailabilityId) { this.id = (int) facilityAvailabilityId; } /** * @return the timeStampSet */ public Date getTimeStampSet() { return timeStampSet; } /** * @param timeStampSet the timeStampSet to set */ public void setTimeStampSet(Date timeStampSet) { this.timeStampSet = timeStampSet; } /** * @param facilityKey the facilityKey to set */ public void setFacilityKey(String facilityKey) { this.facilityKey = facilityKey; } /** * @return the available */ public Integer getAvailable() { return available; } /** * return true, if it is maybe available * * @see FacilityAvailability#MAYBE_AVAILABLE * @return true, if it is maybe available */ public boolean isMaybeAvailable() { return this.availableIs(MAYBE_AVAILABLE); } private boolean availableIs(int availability) { return this.available != null && this.available == availability; } /** * return true, if the booking cannot start here. * * @return true, if the booking cannot start here. */ public boolean mustNotStartHere() { return this.availableIs(BOOKING_MUST_NOT_START_HERE); } /** * @param available the available to set */ public void setAvailable(Integer available) { this.available = available; } /** {@inheritDoc} */ @Override public boolean isCompletelyAvailable() { return this.availableIs(COMPLETE_AVAILABLE); } /** * return true, if it is not available in general * * @see FacilityAvailability#GENERAL_NOT_AVAILABLE * @return true, if it is not available in general */ public boolean isNotAvailableInGeneral() { return this.availableIs(GENERAL_NOT_AVAILABLE); } /** * return true, if it is not available because of a booking * * @see FacilityAvailability#BOOKED_NOT_AVAILABLE * @return true, if it is not available because of a booking */ public boolean isNotAvailableBecauseOfBooking() { return this.availableIs(BOOKED_NOT_AVAILABLE); } /** * return true, if it is not available because of a maintenance * * @see FacilityAvailability#MAINTENANCE_NOT_AVAILABLE * @return true, if it is not available because of a maintenance */ public boolean isNotAvailableBecauseOfMaintenance() { return this.availableIs(MAINTENANCE_NOT_AVAILABLE); } /** * set not available because of booking * * @see FacilityAvailability#BOOKED_NOT_AVAILABLE */ public void setNotAvailableBecauseOfBooking() { this.available = BOOKED_NOT_AVAILABLE; } /** * set not available in general * * @see FacilityAvailability#GENERAL_NOT_AVAILABLE */ public void setNotAvailableInGeneral() { this.available = GENERAL_NOT_AVAILABLE; } /** * set maybe available * * @see FacilityAvailability#MAYBE_AVAILABLE */ public void setMaybeAvailable() { this.available = MAYBE_AVAILABLE; } /** * set not available because of a maintenance * * @see FacilityAvailability#MAINTENANCE_NOT_AVAILABLE */ public void setNotAvailableBecauseOfMaintenance() { this.available = MAINTENANCE_NOT_AVAILABLE; } /** * @return the notice */ public String getNotice() { return this.notice; } /** * @param notice the notice to set */ public void setNotice(String notice) { this.notice = this.isNotice(notice) ? notice.trim() : null; } /** * return the facility the availability is set for. * * @return the facility the availability is set for */ public Facility getFacility() { return FacilityConfigDao.facility(this.getFacilityKey()); } private boolean isNotice(String notize) { return notize != null && !notize.trim().isEmpty(); } /** * return same as {@link #isNotAvailableBecauseOfSuddenFailure()} * * @return same as {@link #isNotAvailableBecauseOfSuddenFailure()} */ public boolean isFailure() { return this.isNotAvailableBecauseOfSuddenFailure(); } /** * set not available because of a sudden failure * * @see FacilityAvailability#SUDDEN_FAILURE_NOT_AVAILABLE */ public void setNotAvailableBecauseOfSuddenFailure() { this.available = SUDDEN_FAILURE_NOT_AVAILABLE; } /** * return true, if the availability stands for a sudden failure * * @return true, if the availability stands for a sudden failure */ public boolean isNotAvailableBecauseOfSuddenFailure() { return this.availableIs(SUDDEN_FAILURE_NOT_AVAILABLE); } /** * set completely available * * @see FacilityAvailability#COMPLETE_AVAILABLE */ public void setCompletelyAvailable() { this.available = COMPLETE_AVAILABLE; } /** {@inheritDoc} */ @Override public List<IntervalTimeFrame> getIntervalTimeFramesWithNoIteration(TimeFrame fromTo) { return this.getIntervalTimeFrame() == null ? new ArrayList<IntervalTimeFrame>() : this.getIntervalTimeFrame().getIntervalTimeFramesWithNoIteration(fromTo); } /** * return the intervals of the availabilities in single not looped intervals. shrink the set to the availabilities overlapping the given time frame. * * @see IntervalTimeFrame#getIntervalTimeFramesWithNoIteration(de.knurt.heinzelmann.util.time.TimeFrame) * @param fromTo shrink the set to the availabilities overlapping this * @return the intervals of the availabilities in single not looped intervals. */ public List<FacilityAvailability> getFacilityAvailabilitiesWithNoIteration(TimeFrame fromTo) { List<FacilityAvailability> result = new ArrayList<FacilityAvailability>(); if (this.getIntervalTimeFrame() != null) { List<IntervalTimeFrame> itfs = this.getIntervalTimeFrame().getIntervalTimeFramesWithNoIteration(fromTo); for (IntervalTimeFrame itf : itfs) { FacilityAvailability da = this.clone(); da.setIntervalTimeFrame(itf); result.add(da); } } return result; } /** {@inheritDoc} */ @Override public List<TimeFrame> getSingleSimpleTimeFrames(TimeFrame fromTo) { return this.getIntervalTimeFrame() == null ? new ArrayList<TimeFrame>() : this.getIntervalTimeFrame().getSingleSimpleTimeFrames(fromTo); } /** {@inheritDoc} */ @Override public void setHourly() { this.getIntervalTimeFrame().setHourly(); } /** {@inheritDoc} */ @Override public void setWeekly() { this.getIntervalTimeFrame().setWeekly(); } /** * if given time frame overlaps this time frame. * * @see IntervalTimeFrame#overlaps(de.knurt.heinzelmann.util.time.TimeFrame) * @param timeframe to check * @return true, if the availability overlaps with given time frame. */ @Override public boolean overlaps(TimeFrame timeframe) { return this.getIntervalTimeFrame().overlaps(timeframe); } /** * return true, if this facility availability is applicable to the given time frame. at most, this is, if the given time frame overlaps with this * facility availability. if availability is {@link #BOOKING_MUST_NOT_START_HERE}, return true, if given time frame starts on interval time frame. * Otherwise return true, * * @param timeframe to check * @return true, if this facility availability is applicable to the given time frame. */ public boolean applicableTo(TimeFrame timeframe) { if (this.mustNotStartHere()) { boolean result = false; for (TimeFrame overlapping : this.getSingleSimpleTimeFrames(timeframe)) { if (overlapping.contains(timeframe.getCalendarStart().getTime())) { result = true; break; } } return result; } else { return this.overlaps(timeframe); } } /** {@inheritDoc} */ @Override public void setInterval(Integer interval) { this.createIntervalTimeFrameNotSet(); this.getIntervalTimeFrame().setInterval(interval); } /** {@inheritDoc} */ @Override public void setMonthly() { this.getIntervalTimeFrame().setMonthly(); } /** {@inheritDoc} */ @Override public void setYearly() { this.getIntervalTimeFrame().setYearly(); } /** {@inheritDoc} */ @Override public TimeFrame getBasePeriodOfTime() { return this.getIntervalTimeFrame() == null ? null : this.getIntervalTimeFrame().getBasePeriodOfTime(); } /** {@inheritDoc} */ @Override public void setBasePeriodOfTime(TimeFrame basePeriodOfTime) { if (this.getIntervalTimeFrame() == null) { this.setIntervalTimeFrame(new SimpleIntervalTimeFrame(basePeriodOfTime)); } else { this.getIntervalTimeFrame().setBasePeriodOfTime(basePeriodOfTime); } } /** {@inheritDoc} */ @Override public boolean isOneTime() { return this.getIntervalTimeFrame().isOneTime(); } /** {@inheritDoc} */ @Override public int compareTo(TimeFrame o) { return this.getIntervalTimeFrame().compareTo(o); } /** {@inheritDoc} */ @Override public void setCalendarStart(int field, int amount) { this.getIntervalTimeFrame().setCalendarStart(field, amount); } /** {@inheritDoc} */ @Override public Integer getInterval() { return this.getIntervalTimeFrame() == null ? null : this.getIntervalTimeFrame().getInterval(); } /** {@inheritDoc} */ @Override public void setDaily() { this.getIntervalTimeFrame().setDaily(); } /** {@inheritDoc} */ @Override public int compareTo(FacilityAvailability o) { if (this.comparingModus == COMPARE_BY_DATE_SET) { return this.getTimeStampSet().before(o.getTimeStampSet()) ? -1 : 1; } else { return this.getBasePeriodOfTime().getDateStart().before(o.getBasePeriodOfTime().getDateStart()) ? -1 : 1; } } /** * return a clone * * @return a clone */ public IntervalTimeFrame getClone() { IntervalTimeFrame clone = new FacilityAvailability(this.facilityKey, this.getBasePeriodOfTime().getCalendarStart(), this.getBasePeriodOfTime().getCalendarEnd()); clone.setInterval(this.getInterval()); return clone; } /** {@inheritDoc} */ @Override public FacilityAvailability clone() { try { FacilityAvailability result = (FacilityAvailability) super.clone(); IntervalTimeFrame cloneditf = this.intervalTimeFrame.clone(); cloneditf.setBasePeriodOfTime(cloneditf.getBasePeriodOfTime().clone()); result.setIntervalTimeFrame(cloneditf); return result; } catch (CloneNotSupportedException e) { throw new Error("implements Cloneable!"); } } /** * @return the intervalTimeFrame */ private IntervalTimeFrame getIntervalTimeFrame() { return intervalTimeFrame; } /** * @param intervalTimeFrame the intervalTimeFrame to set */ private void setIntervalTimeFrame(IntervalTimeFrame intervalTimeFrame) { this.intervalTimeFrame = intervalTimeFrame; } private void createIntervalTimeFrameNotSet() { if (this.getIntervalTimeFrame() == null) { this.intervalTimeFrame = new SimpleIntervalTimeFrame(); TimeFrame tf = this.intervalTimeFrame.getBasePeriodOfTime(); tf.add(Calendar.YEAR, -1000); this.intervalTimeFrame.setBasePeriodOfTime(tf); } } /** {@inheritDoc} */ @Override public Integer getId() { return id; } /** {@inheritDoc} */ @Override public void setId(Integer id) { this.id = id; } /** * set the time stamp set this to given unix timestamp. * * @param milis unix time stamp. */ public void setTimeStampSet(long milis) { this.setTimeStampSet(new Date(milis)); } /** * return true, if availability is not set yet. * * @return true, if availability is not set yet. */ public boolean isUnset() { return this.available == null; } /** * set booking cannot start here. if a facility must be booked for at least 2 hours, it is not available at 5pm, the facility is available at 4pm - * but a booking cannot start at 4pm. this is only for temporary use of the class. */ public void setBookingMustNotStartHere() { this.available = BOOKING_MUST_NOT_START_HERE; } /** {@inheritDoc} */ @Override public void setComparingModus(int comparingModus) { this.comparingModus = comparingModus; } /** {@inheritDoc} */ @Override public int getComparingModus() { return this.comparingModus; } /** * short for {@link #setBookingMustNotStartHere()} */ public void setMustNotStartHere() { this.setBookingMustNotStartHere(); } }