/**
* 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/>.
*/
package org.fenixedu.academic.domain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.StringUtils;
import org.fenixedu.academic.FenixEduAcademicConfiguration;
import org.fenixedu.academic.domain.accounting.AccountingTransaction;
import org.fenixedu.academic.domain.accounting.events.AnnualEvent;
import org.fenixedu.academic.domain.accounting.events.gratuity.DfaGratuityEvent;
import org.fenixedu.academic.domain.candidacy.StudentCandidacy;
import org.fenixedu.academic.domain.candidacy.degree.ShiftDistribution;
import org.fenixedu.academic.domain.degree.DegreeType;
import org.fenixedu.academic.domain.exceptions.DomainException;
import org.fenixedu.academic.domain.time.calendarStructure.AcademicInterval;
import org.fenixedu.academic.domain.time.calendarStructure.AcademicYearCE;
import org.fenixedu.academic.util.PeriodState;
import org.fenixedu.bennu.core.domain.Bennu;
import org.joda.time.DateMidnight;
import org.joda.time.DateTime;
import org.joda.time.DateTimeFieldType;
import org.joda.time.Interval;
import org.joda.time.LocalDate;
import org.joda.time.Partial;
import org.joda.time.YearMonthDay;
import pt.ist.fenixframework.dml.runtime.RelationAdapter;
/**
* Created on 11/Fev/2003
*
* @author Jo�o Mota ciapl Dominio
*
*/
public class ExecutionYear extends ExecutionYear_Base implements Comparable<ExecutionYear> {
static {
getRelationExecutionPeriodExecutionYear().addListener(new ExecutionPeriodExecutionYearListener());
}
static final public Comparator<ExecutionYear> COMPARATOR_BY_YEAR = new Comparator<ExecutionYear>() {
@Override
public int compare(ExecutionYear o1, ExecutionYear o2) {
return o1.getYear().compareTo(o2.getYear());
}
};
static final public Comparator<ExecutionYear> REVERSE_COMPARATOR_BY_YEAR = new Comparator<ExecutionYear>() {
@Override
public int compare(ExecutionYear o1, ExecutionYear o2) {
return -COMPARATOR_BY_YEAR.compare(o1, o2);
}
};
private ExecutionYear() {
super();
setRootDomainObjectForExecutionYear(Bennu.getInstance());
}
public ExecutionYear(AcademicInterval academicInterval, String year) {
this();
setAcademicInterval(academicInterval);
setBeginDateYearMonthDay(academicInterval.getBeginYearMonthDayWithoutChronology());
setEndDateYearMonthDay(academicInterval.getEndYearMonthDayWithoutChronology());
setYear(year);
}
public String getYear() {
return getName();
}
public void setYear(String year) {
if (year == null || StringUtils.isEmpty(year.trim())) {
throw new DomainException("error.ExecutionYear.empty.year");
}
super.setName(year);
}
@Override
public String getQualifiedName() {
return getName();
}
public Collection<ExecutionDegree> getExecutionDegreesByType(final DegreeType degreeType) {
return CollectionUtils.select(getExecutionDegreesSet(), new Predicate() {
@Override
public boolean evaluate(Object arg0) {
ExecutionDegree executionDegree = (ExecutionDegree) arg0;
return executionDegree.getDegreeCurricularPlan().getDegreeType() == degreeType;
}
});
}
public ExecutionYear getNextExecutionYear() {
AcademicYearCE year = getAcademicInterval().plusYear(1);
return getExecutionYear(year);
}
public ExecutionYear getPreviousExecutionYear() {
AcademicYearCE year = getAcademicInterval().minusYear(1);
return getExecutionYear(year);
}
public ExecutionYear getPreviousExecutionYear(final Integer previousCivilYears) {
if (previousCivilYears >= 0) {
AcademicYearCE year = getAcademicInterval().minusYear(previousCivilYears);
return getExecutionYear(year);
}
return null;
}
public boolean hasPreviousExecutionYear() {
return getPreviousExecutionYear() != null;
}
public boolean hasNextExecutionYear() {
return getNextExecutionYear() != null;
}
@Override
public int compareTo(ExecutionYear object) {
if (object == null) {
return 1;
}
return getAcademicInterval().getStartDateTimeWithoutChronology().compareTo(
object.getAcademicInterval().getStartDateTimeWithoutChronology());
}
public boolean isAfter(final ExecutionYear executionYear) {
return this.compareTo(executionYear) > 0;
}
public boolean isAfterOrEquals(final ExecutionYear executionYear) {
return this.compareTo(executionYear) >= 0;
}
public boolean isBefore(final ExecutionYear executionYear) {
return this.compareTo(executionYear) < 0;
}
public boolean isBeforeOrEquals(final ExecutionYear executionYear) {
return this.compareTo(executionYear) <= 0;
}
public boolean isInclusivelyBetween(final ExecutionYear y1, final ExecutionYear y2) {
return (isAfterOrEquals(y1) && isBeforeOrEquals(y2));
}
public boolean isExclusivelyBetween(final ExecutionYear y1, final ExecutionYear y2) {
return (isAfter(y1) && isBefore(y2));
}
public Collection<ExecutionDegree> getExecutionDegreesSortedByDegreeName() {
final List<ExecutionDegree> executionDegrees = new ArrayList<ExecutionDegree>(getExecutionDegreesSet());
Collections.sort(executionDegrees, ExecutionDegree.EXECUTION_DEGREE_COMPARATORY_BY_DEGREE_TYPE_AND_NAME);
return executionDegrees;
}
public ExecutionSemester getExecutionSemesterFor(final Integer semester) {
for (final ExecutionSemester executionSemester : getExecutionPeriodsSet()) {
if (executionSemester.isFor(semester)) {
return executionSemester;
}
}
return null;
}
public ExecutionSemester getFirstExecutionPeriod() {
return Collections.min(this.getExecutionPeriodsSet(), ExecutionSemester.COMPARATOR_BY_SEMESTER_AND_YEAR);
}
public ExecutionSemester getLastExecutionPeriod() {
return Collections.max(this.getExecutionPeriodsSet(), ExecutionSemester.COMPARATOR_BY_SEMESTER_AND_YEAR);
}
public List<ExecutionSemester> readNotClosedPublicExecutionPeriods() {
final List<ExecutionSemester> result = new ArrayList<ExecutionSemester>();
for (final ExecutionSemester executionSemester : getExecutionPeriodsSet()) {
if (!executionSemester.isClosed() && !executionSemester.isNotOpen()) {
result.add(executionSemester);
}
}
return result;
}
public ExecutionSemester readExecutionPeriodByName(final String name) {
for (final ExecutionSemester executionSemester : getExecutionPeriodsSet()) {
if (executionSemester.getName().equals(name)) {
return executionSemester;
}
}
return null;
}
public String getNextYearsYearString() {
final int yearPart1 = Integer.parseInt(getYear().substring(0, 4)) + 1;
final int yearPart2 = Integer.parseInt(getYear().substring(5, 9)) + 1;
return Integer.toString(yearPart1) + getYear().charAt(4) + Integer.toString(yearPart2);
}
public DegreeInfo getDegreeInfo(final Degree degree) {
for (final DegreeInfo degreeInfo : getDegreeInfosSet()) {
if (degreeInfo.getDegree() == degree) {
return degreeInfo;
}
}
return null;
}
public boolean containsDate(final DateTime dateTime) {
final DateMidnight begin = getBeginDateYearMonthDay().toDateMidnight();
final DateMidnight end = getEndDateYearMonthDay().plusDays(1).toDateMidnight();
return new Interval(begin, end).contains(dateTime);
}
public boolean overlapsInterval(final Interval interval) {
final DateMidnight begin = getBeginDateYearMonthDay().toDateMidnight();
final DateMidnight end = getEndDateYearMonthDay().plusDays(1).toDateMidnight();
return new Interval(begin, end).overlaps(interval);
}
public boolean containsDate(final LocalDate date) {
return !getBeginDateYearMonthDay().isAfter(date) && !getEndDateYearMonthDay().isBefore(date);
}
public List<ExecutionDegree> getExecutionDegreesFor(final DegreeType... degreeTypes) {
final List<ExecutionDegree> result = new ArrayList<ExecutionDegree>();
final List<DegreeType> degreeTypesList = Arrays.asList(degreeTypes);
for (final ExecutionDegree executionDegree : getExecutionDegreesSet()) {
if (degreeTypesList.contains(executionDegree.getDegreeCurricularPlan().getDegree().getDegreeType())) {
result.add(executionDegree);
}
}
return result;
}
@Override
public boolean isCurrent() {
return this.getState().equals(PeriodState.CURRENT);
}
public boolean isOpen() {
return getState().equals(PeriodState.OPEN);
}
public boolean isClosed() {
return getState().equals(PeriodState.CLOSED);
}
private boolean isNotOpen() {
return getState().equals(PeriodState.NOT_OPEN);
}
public boolean isFor(final String year) {
return getYear().equals(year);
}
public ShiftDistribution createShiftDistribution() {
return new ShiftDistribution(this);
}
public void delete() {
if (!getExecutionDegreesSet().isEmpty()) {
throw new Error("cannot.delete.execution.year.because.execution.degrees.exist");
}
if (!getStudentGroupSet().isEmpty()) {
throw new DomainException("error.executionYear.cannotDeleteExecutionYearUsedInAccessControl");
}
if (!getTeacherGroupSet().isEmpty()) {
throw new DomainException("error.executionYear.cannotDeleteExecutionYearUsedInAccessControl");
}
if (!getStudentsConcludedInExecutionYearGroupSet().isEmpty()) {
throw new DomainException("error.executionYear.cannotDeleteExecutionYearUsedInAccessControl");
}
for (; !getExecutionPeriodsSet().isEmpty(); getExecutionPeriodsSet().iterator().next().delete()) {
;
}
setRootDomainObject(null);
setRootDomainObjectForExecutionYear(null);
deleteDomainObject();
}
public boolean belongsToCivilYear(int civilYear) {
return (getBeginCivilYear() == civilYear || getEndCivilYear() == civilYear);
}
public int getBeginCivilYear() {
return getBeginDateYearMonthDay().getYear();
}
public int getEndCivilYear() {
return getEndDateYearMonthDay().getYear();
}
public boolean belongsToCivilYearInterval(int beginCivilYear, int endCivilYear) {
for (int year = beginCivilYear; year <= endCivilYear; year++) {
if (belongsToCivilYear(year)) {
return true;
}
}
return false;
}
public boolean isBeforeCivilYear(int civilYear) {
return getEndCivilYear() < civilYear;
}
public boolean isAfterCivilYear(int civilYear) {
return getBeginCivilYear() > civilYear;
}
public int getDistanceInCivilYears(final ExecutionYear executionYear) {
if (executionYear == null || executionYear == this) {
return 0;
}
return Math.abs(getBeginCivilYear() - executionYear.getBeginCivilYear());
}
public Collection<DegreeCurricularPlan> getDegreeCurricularPlans() {
final Collection<DegreeCurricularPlan> result = new HashSet<DegreeCurricularPlan>();
for (final ExecutionDegree executionDegree : getExecutionDegreesSet()) {
result.add(executionDegree.getDegreeCurricularPlan());
}
return result;
}
private Set<AccountingTransaction> getPaymentsFor(final Class<? extends AnnualEvent> eventClass) {
final Set<AccountingTransaction> result = new HashSet<AccountingTransaction>();
for (final AnnualEvent each : getAnnualEventsSet()) {
if (eventClass.equals(each.getClass()) && !each.isCancelled()) {
result.addAll(each.getNonAdjustingTransactions());
}
}
return result;
}
public Set<AccountingTransaction> getDFAGratuityPayments() {
return getPaymentsFor(DfaGratuityEvent.class);
}
public List<StudentCandidacy> getStudentCandidacies() {
final List<StudentCandidacy> result = new ArrayList<StudentCandidacy>();
for (final ExecutionDegree executionDegree : getExecutionDegreesSet()) {
result.addAll(executionDegree.getStudentCandidaciesSet());
}
return result;
}
private static class ExecutionPeriodExecutionYearListener extends RelationAdapter<ExecutionYear, ExecutionSemester> {
@Override
public void beforeAdd(ExecutionYear executionYear, ExecutionSemester executionSemester) {
if (executionYear != null && executionSemester != null && executionYear.getExecutionPeriodsSet().size() == 2) {
throw new DomainException("error.ExecutionYear.exceeded.number.of.executionPeriods", executionYear.getYear());
}
}
}
public ExecutionSemester getExecutionSemester(final YearMonthDay date) {
for (final ExecutionSemester semester : getExecutionPeriodsSet()) {
if (semester.containsDay(date)) {
return semester;
}
}
return null;
}
// -------------------------------------------------------------
// read static methods
// -------------------------------------------------------------
public static ExecutionYear getExecutionYear(AcademicYearCE entry) {
if (entry != null) {
entry = (AcademicYearCE) entry.getOriginalTemplateEntry();
for (final ExecutionYear executionYear : Bennu.getInstance().getExecutionYearsSet()) {
if (executionYear.getAcademicInterval().getAcademicCalendarEntry().equals(entry)) {
return executionYear;
}
}
}
return null;
}
static public ExecutionYear readCurrentExecutionYear() {
ExecutionSemester semester = ExecutionSemester.readActualExecutionSemester();
if (semester != null) {
return semester.getExecutionYear();
} else {
return null;
}
}
static transient private ExecutionYear startExecutionYearForOptionalCurricularCoursesWithLessTenEnrolments = null;
static private ExecutionYear readFromProperties(ExecutionYear executionYear, String yearString) {
if (executionYear == null || executionYear.getRootDomainObject() != Bennu.getInstance()) {
if (yearString == null || yearString.length() == 0) {
executionYear = null;
} else {
executionYear = readExecutionYearByName(yearString);
}
}
return executionYear;
}
public static ExecutionYear readStartExecutionYearForOptionalCurricularCoursesWithLessTenEnrolments() {
startExecutionYearForOptionalCurricularCoursesWithLessTenEnrolments =
readFromProperties(startExecutionYearForOptionalCurricularCoursesWithLessTenEnrolments, FenixEduAcademicConfiguration
.getConfiguration().getStartExecutionYearForAllOptionalCurricularCoursesWithLessTenEnrolments());
return startExecutionYearForOptionalCurricularCoursesWithLessTenEnrolments;
}
static public List<ExecutionYear> readOpenExecutionYears() {
final List<ExecutionYear> result = new ArrayList<ExecutionYear>();
for (final ExecutionYear executionYear : Bennu.getInstance().getExecutionYearsSet()) {
if (executionYear.isOpen()) {
result.add(executionYear);
}
}
return result;
}
static public List<ExecutionYear> readNotClosedExecutionYears() {
final List<ExecutionYear> result = new ArrayList<ExecutionYear>();
for (final ExecutionYear executionYear : Bennu.getInstance().getExecutionYearsSet()) {
if (!executionYear.isClosed()) {
result.add(executionYear);
}
}
return result;
}
public static List<ExecutionYear> readNotOpenExecutionYears() {
final List<ExecutionYear> result = new ArrayList<ExecutionYear>();
for (final ExecutionYear executionYear : Bennu.getInstance().getExecutionYearsSet()) {
if (executionYear.isNotOpen()) {
result.add(executionYear);
}
}
return result;
}
static public List<ExecutionYear> readExecutionYears(ExecutionYear startYear, ExecutionYear endYear) {
final List<ExecutionYear> result = new ArrayList<ExecutionYear>();
result.add(startYear);
ExecutionYear year = startYear.getNextExecutionYear();
while (year != null && year.isBeforeOrEquals(endYear)) {
result.add(year);
year = year.getNextExecutionYear();
}
return result;
}
static public ExecutionYear readExecutionYearByName(final String year) {
for (final ExecutionYear executionYear : Bennu.getInstance().getExecutionYearsSet()) {
if (executionYear.isFor(year)) {
return executionYear;
}
}
return null;
}
static public ExecutionYear readFirstBolonhaExecutionYear() {
return ExecutionSemester.readFirstBolonhaExecutionPeriod().getExecutionYear();
}
public static class ExecutionYearSearchCache {
private final Map<Integer, Set<ExecutionYear>> map = new HashMap<Integer, Set<ExecutionYear>>();
private Set<ExecutionYear> updateIfNeeded(final Integer year) {
Set<ExecutionYear> result = map.get(year);
// for a given civil year, a maximum of two ExecutionYear can be indexed => must update cache if only one ExecutionYear is cached
if (result == null || result.size() < 2) {
for (final ExecutionYear executionYear : Bennu.getInstance().getExecutionYearsSet()) {
add(executionYear);
}
result = map.get(year);
}
return result;
}
public ExecutionYear findByDateTime(final DateTime dateTime) {
final Integer year = Integer.valueOf(dateTime.getYear());
final Set<ExecutionYear> executionYears = updateIfNeeded(year);
if (executionYears != null) {
for (final ExecutionYear executionYear : executionYears) {
if (executionYear.containsDate(dateTime)) {
return executionYear;
}
}
}
return null;
}
public ExecutionYear findByPartial(final Partial partial) {
final Integer year = Integer.valueOf(partial.get(DateTimeFieldType.year()));
final Set<ExecutionYear> executionYears = updateIfNeeded(year);
if (executionYears != null) {
for (final ExecutionYear executionYear : executionYears) {
if (executionYear.getBeginDateYearMonthDay().getYear() == year) {
return executionYear;
}
}
}
return null;
}
private void add(final ExecutionYear executionYear) {
final Integer year1 = executionYear.getBeginDateYearMonthDay().getYear();
final Integer year2 = executionYear.getEndDateYearMonthDay().getYear();
add(executionYear, year1);
add(executionYear, year2);
}
private void add(final ExecutionYear executionYear, final Integer year) {
Set<ExecutionYear> executionYears = map.get(year);
if (executionYears == null) {
executionYears = new HashSet<ExecutionYear>();
map.put(year, executionYears);
}
executionYears.add(executionYear);
}
}
private static final ExecutionYearSearchCache executionYearSearchCache = new ExecutionYearSearchCache();
static public ExecutionYear readByDateTime(final DateTime dateTime) {
return executionYearSearchCache.findByDateTime(dateTime);
}
static public ExecutionYear readByDateTime(final LocalDate localDate) {
return executionYearSearchCache.findByDateTime(localDate.toDateTimeAtCurrentTime());
}
static public ExecutionYear readByPartial(final Partial partial) {
return executionYearSearchCache.findByPartial(partial);
}
public static ExecutionYear readBy(final YearMonthDay begin, YearMonthDay end) {
for (final ExecutionYear executionYear : Bennu.getInstance().getExecutionYearsSet()) {
if (executionYear.getBeginDateYearMonthDay().isEqual(begin) && executionYear.getEndDateYearMonthDay().isEqual(end)) {
return executionYear;
}
}
return null;
}
static public ExecutionYear getExecutionYearByDate(final YearMonthDay date) {
return readByDateTime(date.toDateTimeAtMidnight());
}
static public List<ExecutionYear> readExecutionYearsByCivilYear(int civilYear) {
final List<ExecutionYear> result = new ArrayList<ExecutionYear>();
for (final ExecutionYear executionYear : executionYearSearchCache.map.get(civilYear)) {
if (executionYear.belongsToCivilYear(civilYear)) {
result.add(executionYear);
}
}
return result;
}
static public ExecutionYear readFirstExecutionYear() {
for (final ExecutionYear executionYear : Bennu.getInstance().getExecutionYearsSet()) {
if (!executionYear.hasPreviousExecutionYear()) {
return executionYear;
}
}
return null;
}
static public ExecutionYear readLastExecutionYear() {
for (final ExecutionYear executionYear : Bennu.getInstance().getExecutionYearsSet()) {
if (!executionYear.hasNextExecutionYear()) {
return executionYear;
}
}
return null;
}
public static ExecutionYear readByAcademicInterval(AcademicInterval academicInterval) {
for (final ExecutionYear executionYear : Bennu.getInstance().getExecutionYearsSet()) {
if (executionYear.getAcademicInterval().equals(academicInterval)) {
return executionYear;
}
}
return null;
}
public ExecutionDegree getExecutionDegreeByAcronym(String acronym) {
for (ExecutionDegree executionDegree : getExecutionDegreesSet()) {
if (executionDegree.getDegree().getSigla().equals(acronym)) {
return executionDegree;
}
}
return null;
}
}