/**
* Copyright © 2002 Instituto Superior Técnico
*
* This file is part of FenixEdu Academic.
*
* FenixEdu Academic is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FenixEdu Academic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FenixEdu Academic. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Created on 14/Out/2003
*
*/
package org.fenixedu.academic.domain;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import org.fenixedu.academic.domain.exceptions.DomainException;
import org.fenixedu.academic.util.date.IntervalTools;
import org.fenixedu.bennu.core.domain.Bennu;
import org.joda.time.Interval;
import org.joda.time.YearMonthDay;
/**
* @author Ana e Ricardo
*
*/
@SuppressWarnings("deprecation")
public class OccupationPeriod extends OccupationPeriod_Base {
private OccupationPeriod() {
super();
setRootDomainObject(Bennu.getInstance());
}
public OccupationPeriod(Interval interval) {
this();
if (interval == null) {
throw new DomainException("error.occupationPeriod.invalid.dates");
}
this.setPeriodInterval(interval);
}
/**
* Constructor that creates and links together several instances, allowing
* for the definition of all the intervals
*
* @param intervals
*/
public OccupationPeriod(Iterator<Interval> intervals) {
this();
if (intervals == null || !intervals.hasNext()) {
throw new DomainException("error.occupationPeriod.invalid.dates");
}
Interval interval = intervals.next();
this.setPeriodInterval(interval);
if (intervals.hasNext()) {
this.setNextPeriod(new OccupationPeriod(intervals));
}
}
/*
* Deprecated Constructors
*/
@Deprecated
public OccupationPeriod(YearMonthDay startDate, YearMonthDay endDate) {
this();
if (startDate == null || endDate == null || startDate.isAfter(endDate)) {
throw new DomainException("error.occupationPeriod.invalid.dates");
}
this.setPeriodInterval(IntervalTools.getInterval(startDate, endDate));
}
@Deprecated
public OccupationPeriod(final YearMonthDay... yearMonthDays) {
this(null, yearMonthDays);
}
@Deprecated
protected OccupationPeriod(final OccupationPeriod previous, final YearMonthDay... yearMonthDays) {
this();
final int l = yearMonthDays.length;
if (yearMonthDays == null || l < 2) {
throw new DomainException("error.occupationPeriod.invalid.dates");
}
final YearMonthDay start = yearMonthDays[0];
final YearMonthDay end = yearMonthDays[1];
this.setPeriodInterval(IntervalTools.getInterval(start, end));
if (l > 2) {
final YearMonthDay[] nextYearMonthDays = new YearMonthDay[l - 2];
System.arraycopy(yearMonthDays, 2, nextYearMonthDays, 0, l - 2);
new OccupationPeriod(this, nextYearMonthDays);
}
setPreviousPeriod(previous);
}
public void setNextPeriodWithoutChecks(OccupationPeriod nextPeriod) {
if (nextPeriod != null && !nextPeriod.getPeriodInterval().isAfter(getPeriodInterval())) {
throw new DomainException("error.occupationPeriod.invalid.nextPeriod");
}
super.setNextPeriod(nextPeriod);
}
public void setPreviousPeriodWithoutChecks(OccupationPeriod previousPeriod) {
if (previousPeriod != null && !previousPeriod.getPeriodInterval().isBefore(getPeriodInterval())) {
throw new DomainException("error.occupationPeriod.invalid.previousPeriod");
}
super.setPreviousPeriod(previousPeriod);
}
@Override
public void setNextPeriod(OccupationPeriod nextPeriod) {
if (!allNestedPeriodsAreEmpty()) {
throw new DomainException("error.occupationPeriod.previous.periods.not.empty");
}
this.setNextPeriodWithoutChecks(nextPeriod);
}
@Override
public void setPreviousPeriod(OccupationPeriod previousPeriod) {
if (!allNestedPeriodsAreEmpty()) {
throw new DomainException("error.occupationPeriod.next.periods.not.empty");
}
this.setPreviousPeriodWithoutChecks(previousPeriod);
}
public Calendar getStartDate() {
if (this.getStart() != null) {
Calendar result = Calendar.getInstance();
result.setTime(this.getStart());
return result;
}
return null;
}
public Calendar getEndDate() {
if (this.getEnd() != null) {
Calendar result = Calendar.getInstance();
result.setTime(this.getEnd());
return result;
}
return null;
}
@Deprecated
public Date getStart() {
return this.getPeriodInterval().getStart().toDate();
}
@Deprecated
public Date getEnd() {
return this.getPeriodInterval().getEnd().toDate();
}
public Calendar getEndDateOfComposite() {
Calendar end = this.getEndDate();
OccupationPeriod period = this.getNextPeriod();
while (period != null) {
end = period.getEndDate();
period = period.getNextPeriod();
}
return end;
}
private boolean intersectPeriods(YearMonthDay start, YearMonthDay end) {
return !getStartYearMonthDay().isAfter(end) && !getEndYearMonthDay().isBefore(start);
}
private boolean containsDay(YearMonthDay day) {
return this.getPeriodInterval().contains(day.toDateTimeAtMidnight());
}
public void delete() {
if (allNestedPeriodsAreEmpty()) {
OccupationPeriod first = getFirstOccupationPeriodOfNestedPeriods();
first.deleteAllNestedPeriods();
}
}
private void deleteAllNestedPeriods() {
OccupationPeriod nextPeriod = getNextPeriod();
super.setNextPeriod(null);
super.setPreviousPeriod(null);
setRootDomainObject(null);
deleteDomainObject();
if (nextPeriod != null) {
nextPeriod.delete();
}
}
public boolean allNestedPeriodsAreEmpty() {
OccupationPeriod firstOccupationPeriod = getFirstOccupationPeriodOfNestedPeriods();
if (!firstOccupationPeriod.isEmpty()) {
return false;
}
while (firstOccupationPeriod.getNextPeriod() != null) {
if (!firstOccupationPeriod.getNextPeriod().isEmpty()) {
return false;
}
firstOccupationPeriod = firstOccupationPeriod.getNextPeriod();
}
return true;
}
private boolean isEmpty() {
return getLessonsSet().isEmpty() && getExecutionDegreesSet().isEmpty();
}
public OccupationPeriod getLastOccupationPeriodOfNestedPeriods() {
OccupationPeriod occupationPeriod = this;
while (occupationPeriod.getNextPeriod() != null) {
occupationPeriod = occupationPeriod.getNextPeriod();
}
return occupationPeriod;
}
private OccupationPeriod getFirstOccupationPeriodOfNestedPeriods() {
OccupationPeriod occupationPeriod = this;
while (occupationPeriod.getPreviousPeriod() != null) {
occupationPeriod = occupationPeriod.getPreviousPeriod();
}
return occupationPeriod;
}
public static OccupationPeriod createOccupationPeriodForLesson(final ExecutionCourse executionCourse,
final YearMonthDay beginDate, final YearMonthDay endDate) {
OccupationPeriod result = null;
boolean ok = true;
for (final CurricularCourse curricularCourse : executionCourse.getAssociatedCurricularCoursesSet()) {
final DegreeCurricularPlan degreeCurricularPlan = curricularCourse.getDegreeCurricularPlan();
for (final ExecutionDegree executionDegree : degreeCurricularPlan.getExecutionDegreesSet()) {
if (executionCourse.getExecutionYear() == executionDegree.getExecutionYear()) {
final OccupationPeriod occupationPeriod =
executionDegree.getPeriodLessons(executionCourse.getExecutionPeriod());
if (result == null) {
result = occupationPeriod;
} else if (!result.isEqualTo(occupationPeriod)) {
ok = false;
}
}
}
}
if (ok && result != null) {
if (result.getStartYearMonthDay().equals(beginDate) && result.getEndYearMonthDayWithNextPeriods().equals(endDate)) {
return result;
}
return createNewPeriodWithExclusions(beginDate, endDate, result);
}
for (final OccupationPeriod occupationPeriod : Bennu.getInstance().getOccupationPeriodsSet()) {
if (occupationPeriod.getNextPeriod() == null && occupationPeriod.getPreviousPeriod() == null
&& occupationPeriod.getStartYearMonthDay().equals(beginDate)
&& occupationPeriod.getEndYearMonthDay().equals(endDate)) {
return occupationPeriod;
}
}
return new OccupationPeriod(beginDate, endDate);
}
private static OccupationPeriod createNewPeriodWithExclusions(final YearMonthDay beginDate, final YearMonthDay endDate,
final OccupationPeriod result) {
final SortedSet<YearMonthDay> dates = new TreeSet<YearMonthDay>();
dates.add(beginDate);
dates.add(endDate);
OccupationPeriod pop = result;
for (OccupationPeriod nop = result.getNextPeriod(); nop != null; pop = nop, nop = nop.getNextPeriod()) {
if (pop.getEndYearMonthDay().isAfter(beginDate) && pop.getEndYearMonthDay().isBefore(endDate)) {
dates.add(pop.getEndYearMonthDay());
}
if (nop.getStartYearMonthDay().isAfter(beginDate) && nop.getStartYearMonthDay().isBefore(endDate)) {
dates.add(nop.getStartYearMonthDay());
}
}
return new OccupationPeriod(dates.toArray(new YearMonthDay[0]));
}
public boolean nestedOccupationPeriodsIntersectDates(YearMonthDay start, YearMonthDay end) {
OccupationPeriod firstOccupationPeriod = this;
while (firstOccupationPeriod != null) {
if (firstOccupationPeriod.intersectPeriods(start, end)) {
return true;
}
firstOccupationPeriod = firstOccupationPeriod.getNextPeriod();
}
return false;
}
public boolean nestedOccupationPeriodsContainsDay(YearMonthDay day) {
OccupationPeriod firstOccupationPeriod = this;
while (firstOccupationPeriod != null) {
if (firstOccupationPeriod.containsDay(day)) {
return true;
}
firstOccupationPeriod = firstOccupationPeriod.getNextPeriod();
}
return false;
}
public boolean isGreater(OccupationPeriod period) {
return this.getPeriodInterval().toDuration().isLongerThan(period.getPeriodInterval().toDuration());
}
public boolean isEqualTo(OccupationPeriod period) {
if (getNextPeriod() != null && period.getNextPeriod() != null) {
return isEqualTo(period.getStartYearMonthDay(), period.getEndYearMonthDay(), period.getNextPeriod()
.getStartYearMonthDay(), period.getNextPeriod().getEndYearMonthDay());
}
return getPeriodInterval().equals(period.getPeriodInterval());
}
public boolean isEqualTo(YearMonthDay start, YearMonthDay end, final YearMonthDay startPart2, final YearMonthDay endPart2) {
final boolean eqStart = getStartYearMonthDay().equals(start);
final boolean eqEnd = getEndYearMonthDay().equals(end);
final boolean eqNextPeriod =
getNextPeriod() != null ? (getNextPeriod().getStartYearMonthDay().equals(startPart2)
&& getNextPeriod().getEndYearMonthDay().equals(endPart2) ? true : false) : true;
return eqStart && eqEnd && eqNextPeriod;
}
public YearMonthDay getEndYearMonthDayWithNextPeriods() {
return getNextPeriod() != null ? getNextPeriod().getEndYearMonthDayWithNextPeriods() : getEndYearMonthDay();
}
public String asString() {
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
return String.format("[%s,%s]", sdf.format(getStartDate().getTime()), sdf.format(getEndDate().getTime()));
}
public Interval getIntervalWithNextPeriods() {
return new Interval(getStartYearMonthDay().toLocalDate().toDateTimeAtStartOfDay(), getEndYearMonthDayWithNextPeriods()
.toLocalDate().toDateTimeAtStartOfDay());
}
/*
* Deprecated getters and setters, meant exclusively for compatibility. New
* clients of the Class should use the interval ones Instead.
*/
@Deprecated
public YearMonthDay getStartYearMonthDay() {
Interval interval = this.getPeriodInterval();
return IntervalTools.getStartYMD(interval);
}
@Deprecated
public YearMonthDay getEndYearMonthDay() {
Interval interval = this.getPeriodInterval();
return IntervalTools.getEndYMD(interval);
}
public List<Interval> getIntervals() {
List<Interval> intervals = new LinkedList<Interval>();
OccupationPeriod period = this;
while (period != null) {
intervals.add(period.getPeriodInterval());
period = period.getNextPeriod();
}
return intervals;
}
public void editDates(Iterator<Interval> intervals) {
this.setPeriodInterval(intervals.next());
if (!intervals.hasNext()) {
this.setNextPeriodWithoutChecks(null);
} else {
if (this.getNextPeriod() != null) {
this.getNextPeriod().editDates(intervals);
} else {
this.setNextPeriodWithoutChecks(new OccupationPeriod(intervals));
}
}
}
}