/** * 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.studentCurriculum; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import org.apache.commons.collections.comparators.ReverseComparator; import org.fenixedu.academic.domain.ExecutionSemester; import org.fenixedu.academic.domain.ExecutionYear; import org.fenixedu.academic.domain.StudentCurricularPlan; import org.fenixedu.academic.domain.curricularRules.CurricularRuleType; import org.fenixedu.academic.domain.curricularRules.ICurricularRule; import org.fenixedu.academic.domain.degree.DegreeType; import org.fenixedu.academic.domain.degreeStructure.CycleCourseGroup; import org.fenixedu.academic.domain.degreeStructure.CycleType; import org.fenixedu.academic.domain.degreeStructure.DegreeModule; import org.fenixedu.academic.domain.degreeStructure.RootCourseGroup; import org.fenixedu.academic.domain.exceptions.DomainException; import org.fenixedu.academic.domain.student.curriculum.Curriculum; import org.joda.time.DateTime; /** * * @author - Shezad Anavarali (shezad@ist.utl.pt) * */ public class RootCurriculumGroup extends RootCurriculumGroup_Base { protected RootCurriculumGroup() { super(); createExtraCurriculumGroup(); createPropaedeuticsCurriculumGroup(); } protected RootCurriculumGroup(StudentCurricularPlan studentCurricularPlan, RootCourseGroup rootCourseGroup, ExecutionSemester executionSemester, CycleType cycleType) { this(); init(studentCurricularPlan, rootCourseGroup, executionSemester, cycleType); createStandaloneCurriculumGroupIfNecessary(); } private void init(StudentCurricularPlan studentCurricularPlan, RootCourseGroup courseGroup, ExecutionSemester executionSemester, CycleType cycleType) { checkParameters(studentCurricularPlan, courseGroup, executionSemester); checkInitConstraints(studentCurricularPlan, courseGroup); setParentStudentCurricularPlan(studentCurricularPlan); setDegreeModule(courseGroup); addChildCurriculumGroups(courseGroup, executionSemester, cycleType); } private void checkParameters(final StudentCurricularPlan studentCurricularPlan, final RootCourseGroup courseGroup, final ExecutionSemester executionSemester) { checkParameters(studentCurricularPlan, courseGroup); if (executionSemester == null) { throw new DomainException("error.studentCurriculum.executionPeriod.cannot.be.null"); } } protected RootCurriculumGroup(StudentCurricularPlan studentCurricularPlan, RootCourseGroup rootCourseGroup, CycleType cycleType) { this(); init(studentCurricularPlan, rootCourseGroup, cycleType); createStandaloneCurriculumGroupIfNecessary(); } private void init(final StudentCurricularPlan studentCurricularPlan, final RootCourseGroup rootCourseGroup, final CycleType cycleType) { checkParameters(studentCurricularPlan, rootCourseGroup); checkInitConstraints(studentCurricularPlan, rootCourseGroup); setParentStudentCurricularPlan(studentCurricularPlan); setDegreeModule(rootCourseGroup); addChildCurriculumGroups(rootCourseGroup, cycleType); } private void checkParameters(final StudentCurricularPlan studentCurricularPlan, final RootCourseGroup rootCourseGroup) { if (studentCurricularPlan == null) { throw new DomainException("error.studentCurriculum.studentCurricularPlan.cannot.be.null"); } if (rootCourseGroup == null) { throw new DomainException("error.studentCurriculum.rootCourseGroup.cannot.be.null"); } } private void addChildCurriculumGroups(final RootCourseGroup rootCourseGroup, final ExecutionSemester executionSemester, CycleType cycle) { if (rootCourseGroup.hasCycleGroups()) { createCycle(rootCourseGroup, executionSemester, cycle); } else { super.addChildCurriculumGroups(rootCourseGroup, executionSemester); } } private void addChildCurriculumGroups(final RootCourseGroup rootCourseGroup, CycleType cycle) { if (rootCourseGroup.hasCycleGroups()) { createCycle(rootCourseGroup, null, cycle); } } private void createCycle(final RootCourseGroup rootCourseGroup, final ExecutionSemester executionSemester, CycleType cycle) { if (cycle == null) { cycle = rootCourseGroup.getDegree().getDegreeType().getFirstOrderedCycleType(); } if (cycle != null) { CurriculumGroupFactory.createGroup(this, rootCourseGroup.getCycleCourseGroup(cycle), executionSemester); } } private void checkInitConstraints(final StudentCurricularPlan studentCurricularPlan, final RootCourseGroup rootCourseGroup) { if (studentCurricularPlan.getDegreeCurricularPlan() != rootCourseGroup.getParentDegreeCurricularPlan()) { throw new DomainException("error.rootCurriculumGroup.scp.and.root.have.different.degreeCurricularPlan"); } } public void setRootCourseGroup(final RootCourseGroup rootCourseGroup) { setDegreeModule(rootCourseGroup); } @Override public void setDegreeModule(DegreeModule degreeModule) { if (degreeModule != null && !(degreeModule instanceof RootCourseGroup)) { throw new DomainException("error.curriculumGroup.RootCurriculumGroup.degreeModuleMustBeRootCourseGroup"); } super.setDegreeModule(degreeModule); } @Override public void setCurriculumGroup(CurriculumGroup curriculumGroup) { if (curriculumGroup != null) { throw new DomainException("error.curriculumGroup.RootCurriculumGroupCannotHaveParent"); } } @Override public boolean isRoot() { return true; } @Override public StudentCurricularPlan getStudentCurricularPlan() { return getParentStudentCurricularPlan(); } public boolean hasStudentCurricularPlan() { return getParentStudentCurricularPlan() != null; } private void createExtraCurriculumGroup() { NoCourseGroupCurriculumGroup.create(NoCourseGroupCurriculumGroupType.EXTRA_CURRICULAR, this); } private void createPropaedeuticsCurriculumGroup() { NoCourseGroupCurriculumGroup.create(NoCourseGroupCurriculumGroupType.PROPAEDEUTICS, this); } private void createStandaloneCurriculumGroupIfNecessary() { if (hasStudentCurricularPlan() && getStudentCurricularPlan().isEmptyDegree()) { NoCourseGroupCurriculumGroup.create(NoCourseGroupCurriculumGroupType.STANDALONE, this); } } public CycleCurriculumGroup getCycleCurriculumGroup(CycleType cycleType) { for (CurriculumModule curriculumModule : getCurriculumModulesSet()) { if (curriculumModule.isCycleCurriculumGroup()) { CycleCurriculumGroup cycleCurriculumGroup = (CycleCurriculumGroup) curriculumModule; if (cycleCurriculumGroup.isCycle(cycleType)) { return cycleCurriculumGroup; } } } return null; } public CycleCurriculumGroup getFirstOrderedCycleCurriculumGroup() { for (final CycleType cycleType : getDegreeType().getOrderedCycleTypes()) { CycleCurriculumGroup cycleCurriculumGroup = getCycleCurriculumGroup(cycleType); if (cycleCurriculumGroup != null) { return cycleCurriculumGroup; } } return null; } public CycleCurriculumGroup getLastOrderedCycleCurriculumGroup() { final SortedSet<CycleCurriculumGroup> cycleCurriculumGroups = new TreeSet<CycleCurriculumGroup>(CycleCurriculumGroup.COMPARATOR_BY_CYCLE_TYPE_AND_ID); cycleCurriculumGroups.addAll(getInternalCycleCurriculumGroups()); return cycleCurriculumGroups.isEmpty() ? null : cycleCurriculumGroups.last(); } public CycleCurriculumGroup getLastConcludedCycleCurriculumGroup() { final SortedSet<CycleCurriculumGroup> cycleCurriculumGroups = new TreeSet<CycleCurriculumGroup>(new ReverseComparator(CycleCurriculumGroup.COMPARATOR_BY_CYCLE_TYPE_AND_ID)); cycleCurriculumGroups.addAll(getInternalCycleCurriculumGroups()); for (final CycleCurriculumGroup curriculumGroup : cycleCurriculumGroups) { if (curriculumGroup.isConcluded()) { return curriculumGroup; } } return null; } public Collection<CycleCurriculumGroup> getCycleCurriculumGroups() { Collection<CycleCurriculumGroup> cycleCurriculumGroups = new HashSet<CycleCurriculumGroup>(); for (CurriculumModule curriculumModule : getCurriculumModulesSet()) { if (curriculumModule.isCycleCurriculumGroup()) { cycleCurriculumGroups.add((CycleCurriculumGroup) curriculumModule); } } return cycleCurriculumGroups; } public DegreeType getDegreeType() { return getStudentCurricularPlan().getDegreeType(); } /** * Must not implement this method depending on * hasConcludedCycle(CycleType,ExecutionYear). Each group knows what * ExecutionYear to invoke by internal invocation of the * getApprovedCurriculumLinesLastExecutionYear() method. * */ public boolean hasConcludedCycle(CycleType cycleType) { for (CycleType degreeCycleType : getDegreeType().getCycleTypes()) { if (cycleType == null || degreeCycleType == cycleType) { if (!checkIfCycleIsConcluded(degreeCycleType)) { return false; } } } return cycleType == null || getDegreeType().getCycleTypes().contains(cycleType); } private boolean checkIfCycleIsConcluded(CycleType cycleType) { final CycleCurriculumGroup cycleCurriculumGroup = getCycleCurriculumGroup(cycleType); return cycleCurriculumGroup != null && cycleCurriculumGroup.isConcluded(); } public boolean hasConcludedCycle(CycleType cycleType, final ExecutionYear executionYear) { for (CycleType degreeCycleType : getDegreeType().getCycleTypes()) { if (cycleType == null || degreeCycleType == cycleType) { if (!checkIfCycleIsConcluded(degreeCycleType, executionYear)) { return false; } } } return cycleType == null || getDegreeType().getCycleTypes().contains(cycleType); } private boolean checkIfCycleIsConcluded(CycleType cycleType, final ExecutionYear executionYear) { final CycleCurriculumGroup cycleCurriculumGroup = getCycleCurriculumGroup(cycleType); return cycleCurriculumGroup != null && cycleCurriculumGroup.isConcluded(executionYear).value(); } /** * Only the DegreeType's CycleTypes should be inspected. * CycleCurriculumGroups of other CycleType might exist and shouldn't be * taken into account. * */ @Override public Curriculum getCurriculum(final DateTime when, final ExecutionYear executionYear) { final Curriculum curriculum = Curriculum.createEmpty(this, executionYear); if (!wasCreated(when)) { return curriculum; } final DegreeType degreeType = getDegreeType(); if (degreeType.hasAnyCycleTypes()) { for (final CycleCurriculumGroup cycleCurriculumGroup : getInternalCycleCurriculumGroups()) { curriculum.add(cycleCurriculumGroup.getCurriculum(when, executionYear)); } } else { curriculum.add(super.getCurriculum(when, executionYear)); } return curriculum; } @Override public void delete() { getChildCurriculumGroups().forEach(CurriculumGroup::deleteRecursiveEmptyChildGroups); setParentStudentCurricularPlan(null); super.delete(); } @Override public RootCourseGroup getDegreeModule() { return (RootCourseGroup) super.getDegreeModule(); } public boolean hasExternalCycles() { for (final CycleCurriculumGroup cycleCurriculumGroup : getCycleCurriculumGroups()) { if (cycleCurriculumGroup.isExternal()) { return true; } } return false; } @Override public ICurricularRule getMostRecentActiveCurricularRule(final CurricularRuleType ruleType, final ExecutionYear executionYear) { return getDegreeModule().getMostRecentActiveCurricularRule(ruleType, null, executionYear); } public CycleCurriculumGroup getCycleCurriculumGroupFor(final CurriculumModule curriculumModule) { for (final CycleCurriculumGroup cycleCurriculumGroup : getCycleCurriculumGroups()) { if (cycleCurriculumGroup.hasCurriculumModule(curriculumModule)) { return cycleCurriculumGroup; } } return null; } public CycleCourseGroup getCycleCourseGroup(final CurriculumModule curriculumModule) { final CycleCurriculumGroup cycleCurriculumGroup = getCycleCurriculumGroupFor(curriculumModule); return cycleCurriculumGroup != null ? cycleCurriculumGroup.getDegreeModule() : null; } public CycleCourseGroup getCycleCourseGroup(final CycleType cycleType) { return getDegreeModule().getCycleCourseGroup(cycleType); } public List<CycleCurriculumGroup> getInternalCycleCurriculumGroups() { final List<CycleCurriculumGroup> result = new ArrayList<CycleCurriculumGroup>(); for (final CycleCurriculumGroup cycleCurriculumGroup : getCycleCurriculumGroups()) { if (!cycleCurriculumGroup.isExternal()) { result.add(cycleCurriculumGroup); } } return result; } public List<ExternalCurriculumGroup> getExternalCycleCurriculumGroups() { final List<ExternalCurriculumGroup> result = new ArrayList<>(); for (final CycleCurriculumGroup cycleCurriculumGroup : getCycleCurriculumGroups()) { if (cycleCurriculumGroup.isExternal()) { result.add((ExternalCurriculumGroup) cycleCurriculumGroup); } } return result; } public double getDefaultEcts(final ExecutionYear executionYear) { double result = 0d; for (final CycleCurriculumGroup cycleCurriculumGroup : getInternalCycleCurriculumGroups()) { result += cycleCurriculumGroup.getDefaultEcts(executionYear); } return result; } @Override public Set<CurriculumGroup> getAllCurriculumGroups() { Set<CurriculumGroup> result = new HashSet<CurriculumGroup>(); for (final CurriculumModule curriculumModule : getCurriculumModulesSet()) { result.addAll(curriculumModule.getAllCurriculumGroups()); } return result; } @Override public Set<CurriculumGroup> getAllCurriculumGroupsWithoutNoCourseGroupCurriculumGroups() { Set<CurriculumGroup> result = new HashSet<CurriculumGroup>(); for (final CurriculumModule curriculumModule : getCurriculumModulesSet()) { result.addAll(curriculumModule.getAllCurriculumGroupsWithoutNoCourseGroupCurriculumGroups()); } return result; } }